Chapter 3. Population

This chapter explains the population process.

3.1. Population process

When building web applications with Java, one of the commons things you want to do is to translate user input, i.e. request parameters, into server side objects, like objects of your domain model. Population is the process of matching request parameters (and optionally configuration parameters, session attributes and request attributes) with properties of the form bean, converting the request parameters to the target property type and setting the converted values on the form bean.

By default, Ognl is used to get and set properties. It is possible to use a custom populator delegate instead of the default delegate 'nl.openedge.baritus.population.OgnlFieldPopulator'. See chapter six for the details, and this BeanUtils populator as an example.

3.2. Conversion

One of the tasks to be done when populating a java bean from an HTTP request is the conversion of string parameters to Java types (Integers, Dates, etc.). Converters are used for this purpose. Converters are globally registered with the ConverterRegistry.

There are two types of converters, normal converters and locale-sensitive converters. Locale-sensitive converters override normal converters that are registered for the same type. Thus, if both a normal and a locale-sensitive converter are registered for type java.util.Date, the locale-sensitive converter will be used.

Allthough the ConverterRegistry has sensible defaults, you can register other Converters like this:

ConverterRegistry reg = ConverterRegistry.getInstance();
reg.register(new FallbackDateConverter(), Date.class);
reg.register(new FallbackDateConverter(), java.sql.Date.class);
reg.register(new FallbackDateConverter(), Timestamp.class);

Converters must implement either org.apache.commons.beanutils.Converter or org.apache.commons.beanutils.locale.LocaleConverter.

ConverterRegistry is a global registry. Hence, registered converters (and formatters) are shared amongst all controllers.

3.3. Formatting

Besides converting string values to Java types, you often want to format values. Two mechanisms are available for formatting.

  • Using converters. Converters that implement interface nl.openedge.baritus.converters.Formatter are used automatically for formatting values of the type that the converter was registered for.

  • Other formatters. Formatters (that implement interface nl.openedge.baritus.converters.Formatter) can registered on name. For example:

    public class InverseFormatter implements Formatter
    {
    	/** reverse input */
    	public String format(Object object, String pattern)
    	{
    		String formatted = null;
    		if(object != null)
    		{
    			StringBuffer b = new StringBuffer(String.valueOf(object));
    			formatted = b.reverse();
    		}
    		return formatted;
    	}
    }
    

    ...
    ConverterRegistry reg = ConverterRegistry.getInstance();
    reg.register(new InverseFormatter(), "*REVERSE");
    ...
    

    #set( $myString = "toBeReversed" )
    $!{model.format($myString, "*REVERSE")}
    

    or, if the form bean has property 'myProperty':

    $!{model.displayProperty("myProperty", "*REVERSE")}
    

    Note that displayProperty not only looks up the property value, and checks if there is an override value registered for that property, but also tries to format the value using one of the registered formatters.

The algoritm to find a formatter is: first see if there's a formatter registered on fieldName (e.g. 'myProperty'). If not found, see if there's a formatter registered with the provided pattern (e.g. '*REVERSE). If not found, see if the converter that is registered for the property type and (the optional) locale, implements Formatter. If so, use the converter for formatting. If not found, just convert to a plain string (using ConvertUtils).

3.4. Custom population

The population process can be customized to suit your requirements. By default, class nl.openedge.baritus.population.DefaultFieldPopulator is used for population. DefaultFieldPopulator uses introspection to set the properties. You can however, create custom populators by creating implementations of nl.openedge.baritus.population.FieldPopulator. nl.openedge.baritus.population.AbstractFieldPopulator can be used as a base class. You can register custom populators in two ways:

  • Register the populator with a field name like:

    addPopulator("myProperty",  new MySpecialPopulator());
    

    E.g. with:

    <form action="${request.contextType}/mycmd.m">
    	<input type="text" name="myProperty" value="">
    	<input type="text" name="someOtherProperty" value="">
    	<input type="submit" value="submit">
    </form>
    

    In the above example, MySpecialPopulator will be used to handle request parameter 'myProperty', and DefaultFieldPopulator will be used to handle 'someOtherProperty'.

  • Register the populator with a regexp like:

    // block property by regex pattern
    addPopulator(Pattern.compile("(.)*ByRegex$"), new IgnoreFieldPopulator()); 
    

    E.g. with:

    <form action="${request.contextType}/mycmd.m">
    	<input type="text" name="myPropertyByRegex" value="">
    	<input type="text" name="anotherPropertyByRegex" value="">
    	<input type="text" name="someOtherProperty" value="">
    	<input type="submit" value="submit">
    </form>
    

    In this example, IgnoreFieldPopulator will be used to handle request parameters 'myPropertyByRegex' and 'anotherPropertyByRegex', and DefaultFieldPopulator will be used to handle 'someOtherProperty'. IgnoreFieldPopulator is actually shipped with Baritus as it has a common use. When working with pojo that come for instance from your domain model, you potentially introduce a security hazard. A common case is that clients should never be able to change id's directly from the request. Using IgnoreFieldPopulator you can, for instance, block all request parameters that end with '.id'.

Custom populators are registered with a controller. Hence, registering a custom populator for controller A does not have effect on the population for controller B.