Monday, February 28, 2011

Exploring MetaWidget

Exploring Meta…Widget.


Overview

Metawidget is a framework that allows reducing maintenance of specifying UI rules for different platforms and frameworks such as Java Server Faces (JSF), Google Web Toolkit (GWT), Java Server Pages (JSP), Struts, Swing, Android, etc.

One of most interesting aspects is that Metawidget can co-exist with available frameworks and can add extra value. Basically, it is relying on the inspection, extraction and transformation of components and business object’s metadata in order to create reusable User Interfaces.

One of the key aspects of Metawidget is concept of inspectors that responsible of extracting meaningful metadata from business objects. Metawidget comes with a number of default inspectors that allow extracting metadata based on Java reflection, annotations, etc. But what if we want something special?  J

For instance, how about using business rules as the source of meaningful metadata for UI?

Business requirements of imaginary weather site

Lets say, we want to develop a weather forecast site that will use data available from Bureau of Meteorology, and represent them using our UI based ob business rules developed and extendable externally to UI definitions.

Design and implementation


For implementation of business let’s select a toolbox:
·   Weather Data Services available from Australian Bureau of Meteorology;
·   JBoss AS v.5.1.0.GA for app and web server that provides EJB3 services;
·   Drools – for our business rules compiler and server;
·   RichFaces Java Server Faces implementation – a component library for JSF and an advanced framework for easily integrating AJAX capabilities into business applications that provides over 100+ AJAX enabled UI components;
·   JBoss Seam framework for web app controller layer that acts as glue between various frameworks and libraries – specifically web oriented such as JSF. In other words, if you’re developing web applications and don’t have any underlying web framework then think again! J
·   Facelets - one of the best template processing frameworks for web UI.
·   Maven 3 for project build - developers have to have it unless in favour of ANT;
·   …And we need something to link business rules with business objects, metadata, and JSF UI. While we can develop something from scratch – thanks to JSF – it has extensible interface including server side event model based on request-response life cycle – let’s be lazy (in a good meaning) and reuse existing framework – Metawidget that already does most of the things we need – except for business rules – so we still have an opportunity to develop integration code J

Our site can be accessible by using following URL: http://localhost:port/metawidget-demo

And its main page contains search box to type city name (for the purpose of this demo it is limited to NSW and ACT region cities forecasts), and weather details such as current state and weekly forecast:      


Picture 1. Main page of weather forecast site

Again, for the purpose of this demo and showcase of MetaWidget co-existence with other frameworks let’s generate UI based on business rules only for subset of the page above as following:



Picture 2. Main page partially generated by Metawidget & business rules (area #1) and partially by “hand written” Richfaces/Facelets tags (area #2) – as usual.

OK, let’s start from the beginning – we need weather data first.

Weather Data Service


The Bureau of Meteorology provides Data Services for a number of real-time forecast and observation products that can be freely accessed provide it comply with they Copyright Notice. These data service products are made available on the Bureau ftp site ftp://ftp2.bom.gov.au/anon/gen/fwo/. Each file has a date-time stamp indicating when the data file was created. A directory of the product catalogue is also available.  
The BOM has set of XSD and FTP site to download XML files with forecast by BOM arbitrary code that is related to region (e.g. Sydney, Canberra).

The bom-forecast module developed to utilize BOM weather data service. It converts available BOM XSD specifications to Java using JAXB2 spec – so we can marshall/unmarshall XML to java and back by using simple POJO model with JAXB2 annotations.

The interface to access to BOM service defined as following:
package au.gov.bom.weather;

import java.net.URL;

/**
 * BOM Weather service
 *
 * @author Sergiy Litsenko
 * @see http://www.bom.gov.au/catalogue/data-feeds.shtml Weather Data Services
 * @see http://www.bom.gov.au/weather/schema/v1.1/product.xsd  XSD for output data
*/
public interface BomWeatherService {
       /**
        * FTP based service
        */
       public static final String BOM_SERVICE_URL = "ftp://ftp2.bom.gov.au/anon/gen/fwo/";
       // Images URL
       public static final String BOM_IMAGES_URL =  
                    "http://www.bom.gov.au/images/symbols/large/";

       // XML file for Sydney metropolitan area
       public static final String CODE_SYDNEY_REGION_FORECAST = "IDN10064.xml";
      
       // some Sydney region locations
       public static final String [] SYDNEY_REGION =
              {"Sydney","Penrith","Liverpool",
              "Terrey Hills","Richmond","Parramatta","Campbelltown","Bondi"}; 
      
       // XML file for ACT and Canberra metropolitan area
       public static final String CODE_CANBERRA_REGION_FORECAST = "IDN10035.xml";
      
       // Canberra region locations
       public static final String [] ACT_REGION = {"Canberra","Tuggeranong"}; 

       /**
        * weather graphic codes
        */
       public static final int WGC_SUNNY = 1;
       // …
       public static final int WGC_STORM = 16;
      
       /**
        * Get forecast based on BOM supported codes
        * @param code BOM supported codes
        * @return forecast based on BOM supported codes
        * @throws BomWeatherServiceException
        */
       ProductType getForecast (String code) throws BomWeatherServiceException;

       /**
        * Get forecast based on BOM supported codes
        * @param code URI - the service URL and BOM supported code
        * @return forecast based on BOM supported codes
        * @throws BomWeatherServiceException
        */
       ProductType getForecast (URL codeUri) throws BomWeatherServiceException;
      
       /**
        * Get web server URL for image by weather graphic codes
        * @param weatherGraphicCode
        * @return web server URL for image by weather graphic codes
        */
       URL getImage (int weatherGraphicCode);
}

Code 1. BomWeatherService interface

The BOM weather service is not an EJB or WS based – so our bom-forecast module is a simple library/framework that allows downloading XML files from FTP server by code, and converting XML data to java objects only.  Since forecast is based on BOM arbitrary codes it is not that user friendly.
So yes, we need another layer of abstraction from vendor specific model (in our case BOM weather service).

The metawidget-demo-ejb module
The metawidget-demo-ejb module is a missing EJB piece that simplifies and abstracts weather service from vendor specific model.

Firstly, it defines simplified POJO domain object that represents forecast:
package org.sergiy.forecast;

import java.io.Serializable;
import java.net.URL;
import java.util.Date;

/**
 * Forecast POJO
 *
 * @author Sergiy Litsenko
*/
public class Forecast implements Serializable {
       private static final long serialVersionUID = 360510681383034563L;
       // suburb, city, state
       private String locationName;
       // start date of forecast
       private Date startDate;
       // end date of forecast
       private Date endDate;
       // forecast detailed description
       private String forecastDetailedDescription;
       // forecast brief description
       private String forecastBriefDescription;
       // forecast icon code
       private Integer iconCode;
       // forecast icon image URL as string
       private String image;
       // precipitation probability %
       private Integer precipitationProbability;
       // precipitation range, mm, min-max
       private String precipitationRange;
       // air temperature minimum, Celsius
       private Integer airTemperatureMinimum;
       // air temperature maximum, Celsius
       private Integer airTemperatureMaximum;
       // UV alert
       private String uvAlert;
       // fire alert
       private String fireAlert;

//… set/get data access methods
Code 2. Forecast POJO

And then it defines and implements our façade to access underlying weather service vendor in simplified way:

package org.sergiy.forecast;

import java.util.List;

import javax.ejb.EJB;
import javax.ejb.Local;
import javax.ejb.Stateless;

/**
 * Weather service facade to external BOM weather forecast service
 *
 * @author Sergiy Litsenko
 *
 */
@Local
public interface WeatherService {
       /**
        * Get forecast for location: suburb, city, state
        *
        * @param location the suburb, city, state (NSW, ACT)
        * @return forecast for location: suburb, city, state
        * @throws WeatherServiceException
        */
       Forecast getForecast (String location) throws WeatherServiceException;

       /**
        * Get weekly forecast for location: suburb, city, state
        *
        * @param location the suburb, city, state (NSW, ACT)
        * @return ordered collection of forecasts starting from today, tomorrow, ... 
        * @throws WeatherServiceException
        */
       List<Forecast> getWeeklyForecast (String location) throws WeatherServiceException;
}

Code 3. Weather service
Notice that EJB 3 local façade is based on user friendly location rather than BOM arbitrary code.

Obviously, we have several test cases covering BOM weather service data, and our own façade (please see source code).

Enough with data – lets do the Metawidget & business rules integration work with JSF UI.

Business Rules based metadata inspector powered by Drools and Metawidget


The Drools rule engine is advanced and extensible framework managing knowledge, and it is described in full here - http://downloads.jboss.com/drools/docs/5.1.1.34858.FINAL/drools-expert/html_single/index.html#d0e23

The metawidget-demo-rules module
The simplified façade to quickly build DRL based rules and compile rule base:
 
package org.sergiy.metawidget.rules;

import java.io.*;
import org.drools.*;

/**
 * Factory for managing compilation of Drools packages from different sources
 * @author Sergiy Litsenko
 */
public class PackageFactory {
    /**
     * Compiles drl source stream using package builder
     * 
     * @param builder the builder to compile package
     * @param drlStream the DRL source stream
     * @param drlName the full name of DRL stream (for logging purposes)
     */
    public static void compilePackage  (
            PackageBuilder builder, InputStream drlStream, String drlName) {
    // …
    }


    /**
     * Compiles a package from one or more source files (DRL)
     *
     * @param drlFiles source files. Supported file names as strings and as URLs
     * @return compiled package or throws Runtime Exception
     */
    public static Package compilePackage (Object ... drlFiles) {
    // …
    }
    /**
     * Compiles a package from one or more source files - DRL and rule flow
     *
     * @param  ruleFlow the rule flow for drl files or null
     * @param drlFiles source files. Supported file names as strings and as URLs
     * @return compiled package or throws Runtime Exception
     */
    public static Package compilePackage (Object ruleFlow, Object ... drlFiles) {
    // …
    }
    /**
     * Creates rule base from business rule sources (single package)
     *
     * @param  ruleFlow the rule flow for drl files or null
     * @param drlFiles source files. Supported file names as strings and as URLs
     * @return rule base or throws Runtime Exception
     * @throws RuntimeDroolsException
     */
    public static RuleBase createRuleBase  (Object ruleFlow, Object ... drlFiles) {
    // …
    }
    /**
     * Creates rule base from business rule sources (single package)
     *
     * @param drlFiles source files. Supported file names as strings and as URLs
     * @return rule base or throws Runtime Exception
     * @throws RuntimeDroolsException
     */
    public static RuleBase createRuleBase  (Object ... drlFiles) {
    // …
    }
    /**
     * Creates rule base and adds compiled packages
     *
     * @param packages the compiled packages to add.  
     * @return rule base or throws Runtime Exception
     * @throws RuntimeDroolsException
     */
    public static RuleBase createRuleBase  (Package ... packages) {
    // …
    }
    /**
     * Creates rule base if needed and adds compiled packages
     * @param rule base (may be null)
     * @param packages the compiled packages to add. 
     * @return rule base or throws Runtime Exception
     * @throws RuntimeDroolsException
     */
    public static RuleBase addPackages (RuleBase ruleBase, Package ... packages) {
    // …
    }
    /**
     * Runs business rules using rule base to create new working memory
     *
     * @param ruleBase the rule base to use in order to create working memory
     * @param retractFacts if true then retract facts after run
     * @param facts the facts to introduce to working memory
     * @return business rules working memory
     */
    public static WorkingMemory executeRules (
               RuleBase ruleBase, boolean retractFacts, Object... facts) {
    // …
    }
   
    /**
     * Runs business rules using rule base to create new working memory
     *
     * @param ruleBase the rule base to use in order to create working memory
     * @param agendaGroup the agenda group to focus
     * @param retractFacts if true then retract facts after run
     * @param facts the facts to introduce to working memory
     * @return business rules working memory
     */
    public static WorkingMemory executeRules (RuleBase ruleBase, String agendaGroup,
                  boolean retractFacts, Object... facts) {
    // …
    }

    /**
     * Runs business rules using provided working memory
     *
     * @param workingMemory the working memory to use
     * @param retractFacts if true then retract facts after run
     * @param facts the facts to introduce to working memory
     * @return business rules working memory
     */
    public static WorkingMemory executeRules (WorkingMemory workingMemory,
                  boolean retractFacts, Object... facts) {
    // …
    }
   
    /**
     * Runs business rules using provided working memory
     *
     * @param workingMemory the working memory to use
     * @param agendaGroup the agenda group to focus
     * @param retractFacts if true then retract facts after run
     * @param facts the facts to introduce to working memory
     * @return business rules working memory
     */
    public static WorkingMemory executeRules (WorkingMemory workingMemory, String agendaGroup,
                  boolean retractFacts, Object... facts) {
    // …
   }
}
Code 4. Drools based business rules builder

This is general purpose rules builder utility.

View Definition POJO

Considering that we’re dealing with various UI frameworks business rules needs to be shielded to some degree from variety of implementations - business rules need to operate on some kind of intermediate view definition and still might have an access to domain specific object:
package org.sergiy.metawidget.rules;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
 * A view definition for a domain object used by rules
 * @author Sergiy Litsenko
 *
 */
public class ViewDefinition implements Serializable {
       private static final long serialVersionUID = -5759111446846597839L;

       // domain object
       private Object domainObject;
      
       // domain object view properties map: property name,
        // property attributes (attribute name, attribute value)
       private Map<String, Map<String,String>> properties =
                 new HashMap<String, Map<String,String>>();

       // domain object view actions map: action name, property attributes
      // (attribute name, attribute value)
       private Map<String, Map<String,String>> actions =
                 new HashMap<String, Map<String,String>>();
      
       // view name used as context for the domain object (e.g. JSF viewId).
       private String viewName;

       // the inspection state if true then no more business rules applied
       private boolean inspected;
//… getters/setters        
Code 5. View definition POJO

The View Definition object is intermediary between actual domain object, business rules and Metawidget. It is used by business rules to store extracted UI metadata from domain object based on some specific external to application rules.

BusinessRulesInspector

The BusinessRulesInspector is the heart of Metawidget extension to support business rules. It implements Metawidget Inspector and DomInspector<Element> interfaces is responsible to create View Definition object and pass it to business rules to decide what domain object’s UI related metadata shall be extracted. Once business rules executed the ViewDefinition.properties and ViewDefinition.actions  will contain extracted metadata that will be used by Metawidget InspectionResultProcessors and WidgetBuilders further down the track.

Please note that BusinessRulesInspector is intended to inspect domain objects rather than business rules per se. In other words, the decision logic of what metadata to extract is delegated to DROOLS.
package org.sergiy.metawidget.rules;

import org.drools.*;
import org.w3c.dom.*;
import static org.metawidget.inspector.InspectionResultConstants.*;

/**
 * Inspector based on business rules (DROOLS) to evaluate objects
 * @author Sergiy Litsenko
*/
public class BusinessRulesInspector implements Inspector, DomInspector<Element> {
       // configuration
       private BusinessRulesInspectorConfig config;
       // DROOLS rule base
       private RuleBase ruleBase;
      
       /**
        * Create DROOLS rule base based on configuration
        * @return DROOLS rule base
        */
       protected RuleBase createRuleBase (BusinessRulesInspectorConfig config) {
              // drools rule base
              RuleBase ruleBase =
                    PackageFactory.createRuleBase(config.getDroolsSourceFiles().toArray());
              // return
              return ruleBase;
       }
       /**
        * Constructor with config
        * @param  config the business rules config
        */
       public BusinessRulesInspector(BusinessRulesInspectorConfig config) {
              setConfig (config);
              // build rule base here as the only place to initialise
              if (config != null && config.getDroolsSourceFiles() != null) {
                     setRuleBase (createRuleBase (config));
              } else {
                     log.warn ("The BusinessRulesInspectorConfig shall be provided with valid
                                DROOLS files. The BusinessRulesInspector deactivated!");
              }
       }

       /* (non-Javadoc)
        * @see org.metawidget.inspector.iface.Inspector#inspect(java.lang.Object,
       * java.lang.String, java.lang.String[])
        */
       @Override
       public String inspect(Object toInspect, String type, String... names) {
              // inspect as DOM
              Element element = inspectAsDom( toInspect, type, names );
              // if not inspected
              if ( element == null ) {
                     return null;
              }
             
              // convert to string
              return XmlUtils.nodeToString( element, false );
       }

       /* (non-Javadoc)
        * @see org.metawidget.inspector.iface.DomInspector#inspectAsDom(java.lang.Object,
        * java.lang.String, java.lang.String[])
        */
       @Override
       public Element inspectAsDom( Object toInspect, String type, String... names ) {
              // get rule base
              RuleBase rb = getRuleBase();
              // check if not active
              if (rb == null) {
                     return null;
              }
             
              //
              // Inspect a domain object based on DROOLS rules
              //
              ViewDefinition viewDef = new ViewDefinition (toInspect);
             
              // trace
              if (log.isTraceEnabled()) {
                     log.trace(String.format("View Definition - initial state: %1$s. Executing
                                             business rules...", viewDef));
              }
             
              // execute rules
              WorkingMemory wm = PackageFactory.executeRules (
                                       rb, null, false, new Object[]{viewDef});

              //
              // Convert received back ViewDefinition to DOM
              //
             
              // … create document
              Document document = XmlUtils.newDocument();
              // return
              return root;
       }
       /**
        * @return the config
        */
       public BusinessRulesInspectorConfig getConfig() {
              return config;
       }

       /**
        * @return the ruleBase
        */
       public RuleBase getRuleBase() {
              return ruleBase;
       }
       /**
        * @param ruleBase the ruleBase to set
        */
       protected void setRuleBase(RuleBase ruleBase) {
              this.ruleBase = ruleBase;
       }
}
Code 6. Business Rules Inspector

The BusinessRulesInspector has corresponding BusinessRulesInspectorConfig that allows declare the DROOLS file locations:
package org.sergiy.metawidget.rules;

import org.metawidget.inspector.impl.BaseObjectInspectorConfig;
/**
 * Configuration for Business rules based inspector
 * @author Sergiy Litsenko
 */
public class BusinessRulesInspectorConfig extends BaseObjectInspectorConfig {
       // DROOLS source files
       private List<Object> droolsSourceFiles;
       /**
        * Default constructor
        */
       public BusinessRulesInspectorConfig() {
       }
       /**
        * Constructor
        * @param droolsSourceFiles
        */
       public BusinessRulesInspectorConfig(List<Object> droolsSourceFiles) {
              this();
              setDroolsSourceFiles(droolsSourceFiles);
       }
       /**
        * Constructor
        * @param droolsSourceFiles
        */
       public BusinessRulesInspectorConfig(Object... droolsSourceFiles) {
              this(Arrays.asList(droolsSourceFiles));
       }

       /**
        * Get DROOLS source files
        * @return the droolsSourceFiles
        */
       public List<Object> getDroolsSourceFiles() {
              return droolsSourceFiles;
       }
Code 7. Business Rules Inspector configuration object

While the DROOLS has concept of both local and remote knowledge bases based on concept of Knowledge Agent - for the purpose of this demo configuration is limited to local DRL files only.

The Business Rules Inspector is configured in metawidget.xml together with other inspectors:
<?xml version="1.0"?>
<metawidget xmlns="http://metawidget.org"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://metawidget.org http://metawidget.org/xsd/metawidget-1.0.xsd"
       version="1.0">
  <htmlMetawidget xmlns="java:org.metawidget.faces.component.html">
    <inspector>           
       <compositeInspector xmlns="java:org.metawidget.inspector.composite"
                                 config="CompositeInspectorConfig">

         <inspectors>
           <array>
             <propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"/>
              <metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation"/>
              <facesInspector xmlns="java:org.metawidget.inspector.faces"/>
              <businessRulesInspector xmlns="java:org.sergiy.metawidget.rules"
                                     config="BusinessRulesInspectorConfig">
                           <droolsSourceFiles>
                             <array>
                                  <string>ui-rules.drl</string>
                             </array>
                           </droolsSourceFiles>
              </businessRulesInspector>
           </array>
        </inspectors>
       </compositeInspector>
   </inspector>
 …
</htmlMetawidget>
</metawidget>
Code 8. metawidget.xml config

Sample ui-rules.drl located in the /metawidget-demo-web/src/main/webapp/WEB-INF/classes directory actually defining how in our case with weather site we going to display Forecast POJO (see code.2):
package ui;
dialect "java"

import org.sergiy.metawidget.rules.ViewDefinition;
import org.sergiy.forecast.Forecast;

rule "View Definition rule - main view"
  salience  1
when
  vd : ViewDefinition (inspected == false,
                       do: domainObject,
                       domainObjectClassName matches "org.sergiy.forecast.Forecast")
then
  // set view object properties
  vd.setPropertyAttribute ("locationName", "label", "Location");
  vd.setPropertyAttribute ("locationName", "read-only", "true");
  vd.setPropertyAttribute ("locationName", "type", "java.lang.String");

  vd.setPropertyAttribute ("startDate", "label", "Forecast for");
  vd.setPropertyAttribute ("startDate", "read-only", "true");
  vd.setPropertyAttribute ("startDate", "type", "java.util.Date");
  vd.setPropertyAttribute ("startDate", "date-style", "full");
  vd.setPropertyAttribute ("startDate", "comes-after", "locationName");

  vd.setPropertyAttribute ("endDate", "hidden", "true");
  vd.setPropertyAttribute ("endDate", "type", "java.util.Date");

  vd.setPropertyAttribute ("image", "label", "Image");
  vd.setPropertyAttribute ("image", "type", "java.net.URL");
  vd.setPropertyAttribute ("image", "hidden", "true");

  vd.setPropertyAttribute ("airTemperatureMaximum", "label", "Max");
  vd.setPropertyAttribute ("airTemperatureMaximum", "read-only", "true");
  vd.setPropertyAttribute ("airTemperatureMaximum", "type", "java.lang.Integer");
  vd.setPropertyAttribute ("airTemperatureMaximum", "comes-after", "startDate");


  vd.setPropertyAttribute ("airTemperatureMinimum", "label", "Min");
  vd.setPropertyAttribute ("airTemperatureMinimum", "read-only", "true");
  vd.setPropertyAttribute ("airTemperatureMinimum", "type", "java.lang.Integer");
  vd.setPropertyAttribute ("airTemperatureMinimum", "comes-after", "airTemperatureMaximum");

  vd.setPropertyAttribute ("temperatureRangeAsString", "type", "java.lang.String");
  vd.setPropertyAttribute ("temperatureRangeAsString", "hidden", "true");

  vd.setPropertyAttribute ("forecastBriefDescription", "label", "Forecast");
  vd.setPropertyAttribute ("forecastBriefDescription", "read-only", "true");
  vd.setPropertyAttribute ("forecastBriefDescription", "type", "java.lang.String");
  vd.setPropertyAttribute ("forecastBriefDescription", "comes-after",
                                 "airTemperatureMinimum");

  vd.setPropertyAttribute ("precipitationProbability", "label", "Chance of any rain");
  vd.setPropertyAttribute ("precipitationProbability", "read-only", "true");
  vd.setPropertyAttribute ("precipitationProbability", "type", "java.lang.Integer");
  vd.setPropertyAttribute ("precipitationProbability", "number-pattern", "# '%'");
  vd.setPropertyAttribute ("precipitationProbability", "comes-after",
                                    "forecastBriefDescription");

  vd.setPropertyAttribute ("precipitationRange", "label", "Rainfall amount");
  vd.setPropertyAttribute ("precipitationRange", "read-only", "true");
  vd.setPropertyAttribute ("precipitationRange", "type", "java.lang.String");
  vd.setPropertyAttribute ("precipitationRange", "comes-after", "precipitationProbability");

  vd.setPropertyAttribute ("fireAlert", "label", "Fire Danger");
  vd.setPropertyAttribute ("fireAlert", "read-only", "true");
  vd.setPropertyAttribute ("fireAlert", "type", "java.lang.String");
  vd.setPropertyAttribute ("fireAlert", "comes-after", "precipitationRange");

  vd.setPropertyAttribute ("uvAlert", "label", "UV Alert");
  vd.setPropertyAttribute ("uvAlert", "read-only", "true");
  vd.setPropertyAttribute ("uvAlert", "type", "java.lang.String");
  vd.setPropertyAttribute ("uvAlert", "comes-after", "fireAlert");

  vd.setPropertyAttribute ("forecastDetailedDescription", "label", "Details");
  vd.setPropertyAttribute ("forecastDetailedDescription", "read-only", "true");
  vd.setPropertyAttribute ("forecastDetailedDescription", "type", "java.lang.String");
  vd.setPropertyAttribute ("forecastDetailedDescription", "comes-after", "uvAlert");
 
  vd.setPropertyAttribute ("iconCode", "type", "java.lang.Integer");
  vd.setPropertyAttribute ("iconCode", "hidden", "true");

  // mark as inspected
  vd.setInspected(true);
 
  // propagate changes
  update (vd);
end
Code 9. ui-rules.drl
Please note that Forecast domain object has different order of properties declared to compare with what we want to appear on the web page – so the rule sets “comes-after” and other properties such as “date-style” properties utilized by other Metawidget component in order to produce JSF view as following:

 Picture 3 JSF view of forecast POJO

This is produced by metawidget-demo-web/src/main/webapp/index.xhtml page:



<ui:composition xmlns="http://www.w3.org/1999/xhtml"
              xmlns:s="http://jboss.com/products/seam/taglib"
              xmlns:ui="http://java.sun.com/jsf/facelets"
              xmlns:f="http://java.sun.com/jsf/core"
              xmlns:h="http://java.sun.com/jsf/html"
              xmlns:rich="http://richfaces.org/rich"
              xmlns:a4j="http://richfaces.org/a4j"
              xmlns:richx="http://richfaces.org/richx"
                xmlns:m="http://metawidget.org/faces"
              template="layout/template.xhtml">
              <div>
                     <m:metawidget value="#{forecaster.todayForecast}"/>
              </div>
..
Code 10. Fragment of index.xhtml page

Fore more details please see metawidget-demo-web module.

Maven based build

Simply unzip the distributed source code to some directory and from that directory execute:

mvn clean install

And wait until following message:

 
Picture 4 Maven build process

The project source code and EAR file located here: http://weather-forecast-jsf-demo.googlecode.com/svn/trunk/ 

Deployment to JBOSS AS

Simply copy the builded EAR file to /jboss-5.1.0.GA/server/<profile name>/deploy directory.
It has been tested with “standard” profile.