Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3
Panel
borderStylesolid
titleTable of contents
Table of Contents
minLevel2
Panel

NOTE: this page is about models like they exist for Wicket 1.x. The IModel interface is slightly changed in Wicket 2.x. Most of what is written on this page applies to Wicket 2.x as well, but where it doesn't there will be notes.

What are Wicket Models?

In Wicket like in other view frameworks , a model provides the data holds a value for a component should show to display and/or edit. How the model-concept is implemented and used is different between different view frameworks. Ie in Swing, you have a number of component specific model-interfaces or in Struts the model is represented by a bean and there is no explicit model-interface.In Wicket there is only one generic model interface (exactly this value is held is determined by a given model's implementation of the wicket.model.IModel). Each component in wicket takes an instance of this one and only IModel interface. Ie a Label, a TextField, a ListView each take an IModel instance.

All an IModel does is provide a way to get and set a value for a component - nothing more. What this value is depends on the component. Ie. for a Label it must be something, which can be converted to a String. For a ListView it must be a java.util.List. An IModel just binds a wicket component to a component-specific value. Outside of wicket this concept is therefore often called a binding. You could also say Wicket comes with its integrated binding-framework.

This has a number of advantages:

  • There are default IModel implentations you can use on all different components (you will see later). Ie there is a powerful PropertyModel to work on beans and more.
  • You do not have to implement an extra interface or helper class for each different component as in Swing. Especially for the most often used components like Labels and TextFields you can easily bind to a bean property.
  • If you do not wish to implement an IModel you can provide the demanded value for the component directly and the component will do a default IModel binding internally.
  • And while you are not tied to using beans as your models as in Struts, you may still easily use beans.

Finally, for web-apps, an IModel has another big advantage. If you extend your model from wicket.model.AbstractDetachableModel the model automatically gets called at the end of the servlet-request to allow it to unload (detach) its value. It is also called to allow it to load (attach) its value the first time the value is accessed in the current servlet-request. When the IModel is set on a component this happens automatically without any easy to forget explicit registration. This way you can transparently use large values (ie Lists) without much concern whether they would require too much memory in the Session. Nearly just like in a client-app.

Simple Models

The HelloWorld example program demonstrates the simplest model type in
Wicket:

Code Block

public class HelloWorld extends WicketExamplePage
{
	public HelloWorld()
	{
		add(new Label("message", "Hello World!"));
	}
}

The first parameter to the Label component added in the constructor is the Wicket id, which associates the Label with a tag in the HelloWorld.html markup file:

Code Block

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns:wicket='http://wicket.sourceforge.net/wicket'>
<head>
	<title>Wicket Examples - helloworld</title>
	<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
	<span wicket:id="mainNavigation"/>
	<span wicket:id="message">Message goes here</span>
</body>
</html>

The second parameter to Label is the model for the Label, which provides content which replaces any text inside the <span> tag that the Label is attached to. The model being passed to the Label constructor above is apparently a String. But if we look inside the Label constructor we see that this is merely a convenience and that the Label constructor wraps its String argument in a Model object like this:

Code Block

public Label(final String id, String label)
{
	this(id, new Model(label));
}

If we look up the inheritance hierarchy for Label, we see that Label extends WebComponent, which in turn extends Component. Each of these superclasses has only two constructors. One Component constructor takes a String id:

Code Block

public Component(final String id)
{
		setId(id);
}

And the other takes a String id and an IModel:

Code Block

public Component(final String id, final IModel model)
{
		setId(id);
		setModel(model);
}

IModel is the interface that must be implemented by all Wicket model objects. It looks like this:

Code Block

public interface IModel extends IDetachable
{
	public Object getNestedModel();
	public Object getObject(final Component component);
	public void setObject(final Component component, final Object object);
}

And the base interface IDetachable looks like this:

Code Block

public interface IDetachable extends Serializable
{
	public void detach();
}

These details are not especially interesting to a beginning model user, so we will come back to this later. What we need to know right now is that Model extends AbstractModel, which implements IModel. So a Model is an IModel and therefore it can be passed to the Component(String, IModel) constructor we saw a moment ago.

If we take a peek inside Model, we'll see that the Model object is simply a wrapper that holds a Serializable object:

Code Block

private Serializable object;

Model's object value is Serializable in order to support deployment of web applications in clustered environments. Note also that since IModel extends IDetachable, which extends Serializable, all IModel wrapper classes are themselves Serializable. So all Wicket models are therefore serializable, which means that they can be replicated between servers in a cluster. Don't worry if you don't fully understand this. We will talk more about clustering below.

Property Models

The PropertyModel class allows you to create a model that accesses a particular property of its nested model object at runtime. This property is accessed using a simple expression language with dot notation (e.g. 'name' means property 'name', and 'person.name' means property name of property person). The simplest PropertyModel constructor is:

Code Block

public PropertyModel(final Object modelObject, final String expression)

which takes a model object and a property expression. When the property model is asked for its value by the framework, it will use the property expression to access the model object's property. For example, if we have a Java Bean or "POJO" (Plain Old Java Object) like this:

Code Block

class Person
{
	private String name;

	Person(String name)
	{
		this.name = name;
	}

	String getName()
	{
		return name;
	}
}

then the property expression "name" can be used to access the "name" property of any Person object via the getName() getter method.

Nested property expressions are possible as well. You can access sub-properties via reflection using a dotted path notation, which means the property expression "person.name" is equivalent to calling getPerson().getName() on the given model object.

interface. The IModel interface decouples a component from the model object which forms its value. This in turn decouples the whole Wicket framework from any and all details of model storage, such as the details of a given persistence technology. As far as Wicket itself is concerned, a model is anything that implements the IModel interface, no matter how it might do that. Although there are some refinements described below, conceptually, IModel looks like this:

Code Block

public interface IModel
{
	public Object getObject();
	public void setObject(final Object object);
}

The IModel interface defines a simple contract for getting and setting a value. The nature of the Object retrieved or set will depend on the component referencing the model. For a Label component, the value must be something which can be converted to a String which will be displayed when the label is rendered. For a ListView, it must be a java.util.List containing the values to be displayed as a list.

Different frameworks implement the model concept differently. Swing has a number of component-specific model interfaces. Struts requires that the model be a Java Bean and there is no explicit model interface. The IModel interface in Wicket allows models to be generic (as in Struts) but it can do things that would not be possible if components accessed their model directly (as in Swing). For example, Wicket applications can use or provide IModel implementations that read a model value from a resource file or retrieve a model value from a database only when needed.

The use of a single model interface (as compared to having multiple interfaces, or having no model interface at all) has a number of advantages:

  • Wicket provides IModel implementations you can use with any component. These models can do things such as retrieve the value from a resource file, or read and write the value from a Java Bean property.
  • Wicket also provides IModel implementations that defer retrieving the value until it is actually needed, and remove it from the servlet Session when the request is complete. This reduces session memory consumption and is particularly useful with large values such as lists.
  • Unlike Swing, you do not have to implement an extra interface or helper class for each different component. Especially for the most often used components such as Labels and TextFields you can easily bind to a bean property.
  • In many cases you can provide the required value directly to the component and it will wrap a default model implementation around it for you.
  • And while you do not have to use beans as your models as you must with Struts, you may still easily use beans if you wish. Wicket provides the appropriate model implementations.

Simple Models

The HelloWorld example program demonstrates the simplest model type in Wicket:

Code Block

public class HelloWorld extends WicketExamplePage
{
	public HelloWorld()
	{
		add(new Label("message", "Hello World!"));
	}
}

The constructor for this page constructs a Label component. The first parameter to the Label component's constructor is the Wicket id, which associates the Label with a tag in the HelloWorld.html markup file:

Code Block

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns:wicket='http://wicket.sourceforge.net/wicket'>
<head>
	<title>Wicket Examples - helloworld</title>
	<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
	<span wicket:id="mainNavigation"/>
	<span wicket:id="message">Message goes here</span>
</body>
</html>

The second parameter to the Label component's constructor is the model data for the Label, providing content that replaces any text inside the <span> tag to which the Label is associated. The model data passed to the Label constructor above is apparently a String. Internally Label creates a Model for the String. Model is a simple default implementation of IModel.

Thus instead we could have created our label this way:

Code Block

add(new Label("message", new Model("Hello World!")));

The Label constructor that takes a String is simply a convenience.

Dynamic Models

The data we gave to the model in the previous example, the string "Hello World", is constant. No matter how many times Wicket asks for the model data, it will get the same thing. Now consider a slightly more complex example:

Code Block

Label name = new Label ("name", new Model(person.getName()));

The model data is still a String, the value of person.getName() is set at the time the model is created. Recall that Java strings are immutable: this string will never change. Even if person.getName() would later return a different value, the model data is unchanged. So the page will still display the old value to the user even if it is reloaded. Models like this, whose values never change, are known as static models.

In many cases the underlying data can change, and you want the user to see those changes. For example, the user might use a form to change a person's name. Models which can automatically reflect change are known as dynamic models. While the Model class is static, most of the other core Wicket model classes are dynamic.

It's instructive to see how to make a dynamic model by subclassing Model.

Code Block

personForm.add(new RequiredTextField("personName", new Model() {
	@Override
	public Object getObject() {
		return person.getName();
	}

	@Override
	public void setObject(Serializable object) {
		person.setName((String) object);
	}
}));

It would be inconvenient to have to do this for every component that needs a dynamic model. Instead, you can use the PropertyModel class or one of the other classes described below.

Property Models

The PropertyModel class allows you to create a model that accesses a particular property of its associated model object at runtime. This property is accessed using a simple expression language with a dot notation (e.g. 'name' means property 'name', and 'person.name' means property name of object person). The simplest PropertyModel constructor isA second contructor on PropertyModel is available for models that wish to access properties involving type conversions:

Code Block
public PropertyModel(final Object modelObject, final String expression, Class propertyType)

Although the property models will do automatic type conversions for you, it may not always do the kind of conversion you desire. This alternative constructor creates a property model that forces a particular conversion to occur. For example, you may wish to access a String or Object property of something as an Integer. Constructing a PropertyModel with new PropertyModel(object, "myString", Integer.class) would force the property to be converted to and from an Integer on get/set operations.

Compound Property Models

If you were paying close attention earlier, you may have wondered why Components, which generally must have a model, can be constructed with only an id, omitting any model. There are two reasons for this.

The first reason to construct a Component with no model is that it is sometimes impossible to have a model ready for the call to super(String, IModel) in the Component's constructor. In this case, the Component can be constructed without a model, and the model can be later set with a call to setModel().

The second reason to construct a Component with no model is that some Components are actually bound to their models at runtime. When an attempt is made to retrieve the model for a Component which does not yet have one, instead of failing and returning null, the getModel() method in Component calls the method initModel(), which gives the Component an opportunity to lazy-initialize, returning a model on-the-fly.

This mechanism opens up all kinds of possibilities for model initialization. One is particularly convenient and efficient lazy model initialization strategy is employed by the CompoundPropertyModel and BoundCompoundPropertyModel classes to allow containers to share their models with children. By sharing model objects like this, fewer objects are created, which saves memory, but more importantly, it makes replication of models much cheaper in a clustered environment.

To use a CompoundPropertyModel, you simply set one as the model for any container. However, a very typical place to install a CompoundPropertyModel is on a Form or Page. This is natural, because the compound model itself is usually the model that is being displayed by the Page or edited by the Form.

To see a CompoundPropertyModel in action, take a look at the FormInput example. In the InputForm nested class in FormInput.java, the Form is constructed like this:

Code Block

public InputForm(String name, IFeedback feedback)
{
	super(name, new CompoundPropertyModel(new FormInputModel()), feedback);
	...
}

If we look at the next few lines of the Form's constructor, we see statements like:

Code Block

add(new RequiredTextField("stringProperty"));
		add(new RequiredTextField("integerProperty", Integer.class));
		add(new RequiredTextField("doubleProperty", Double.class));

these statements are adding FormComponents to the Form. But they are not explicitly attached to any model. Instead, they will inherit the CompoundPropertyModel for the form at runtime. When the framework needs the model for a RequiredTextField, it will call getModelObjectAsString(). This method retrieves the text field's model by calling getModel(), which is implemented like this:

Code Block

public IModel getModel()
{
	if (model == null)
	{
		this.model = initModel();
	}
	return model;
}

Since the RequiredTextField has no model, the initModel() method will be called, resulting in a search up the containment hierarchy for a CompoundPropertyModel that can be inherited by the model-less child:

Code Block

protected IModel initModel()
{
	for (Component current = getParent(); current != null; current = current.getParent())
	{
		final IModel model = current.getModel();
		if (model instanceof CompoundPropertyModel)
		{
			return model;
		}
	}
	return null;
}

The text field will discover the CompoundPropertyModel for the Form and set that as its own model. So getModel() on the text field ultimately returns the model of the Form.

Next, the nested model object is retrieved from the model by passing in the Component (in this case the RequiredTextField) as a parameter to IModel.getObject(Component). This is the magic that allows the CompoundPropertyModel to retrieve the right value from the Form's model for the Component that is asking for it:

Code Block

final Object modelObject = model.getObject(this);

The retrieval is done by using the name of the Component as the property expression to access the model. In the case of this component

Code Block

add(new RequiredTextField("stringProperty"));

the getStringProperty() method would be called on the Form's model.

The model's object value is available in this way to the component for processing. In the case of the stringProperty text field component, it will convert the value to a String. In the case of other RequiredTextFields in the FormInput form, a conversion may occur based on the third parameter to the Component. For example, this component:

Code Block

add(new RequiredTextField("integerProperty", Integer.class));

will convert the contents of the text field to and from an Integer value.

In the event that you need more flexibility or property expression power in your compound models, you can use the BoundCompoundPropertyModel. This class provides three methods you can call on the container's model object to bind a given child Component to a specific property and/or type conversion:

Code Block

public Component bind(final Component component, final String propertyExpression)
public Component bind(final Component component, final Class type)
public Component bind(final Component component, final String propertyExpression, final Class type)

To see how this might be used, suppose that the stringProperty value needed to be bound to the property expression "person0.stringProperty". gIn the FormInput constructor, we'd say something like this:

Code Block

super(name, feedback);
final BoundCompoundPropertyModel formModel = new BoundCompoundPropertyModel(new FormInputModel());
setModel(formModel);
add(formModel.bind(new RequiredTextField("stringProperty"), "person[0].stringProperty"));

The Form is constructed without a model. We then create the Form's model as a BoundCompoundPropertyModel, set it as the Form's model and finally use it to bind the RequiredTextField to the desired property expression.

String Resource Models

Localization of resources in Wicket is supported by the Localizer class. This provides a very convenient way to retrieve and format application and component specific resources according to the Locale that the user's Session is running under. In many cases, it will be sufficient to simply retrieve a localized String in this way, but if a formatted, localized string is desired as a model, a StringResourceModel may be used. StringResourceModels have a resource key (for looking up the string resource in a property file), a Component, an optional model and an optional array of parameters that can be passed to the Java MessageFormat facility. The constructors look like this:

Code Block

public StringResourceModel(final String resourceKey, final Component component, final IModel model)
public StringResourceModel(final String resourceKey, final Component component, final IModel model, final Object[] parameters)

A very simple example of a StringResourceModel might be a Label constructed like this:

Code Block

add(new Label("greetings", new StringResourceModel("label.greetings", this, null)));

where the resource bundle for the page contains an entry like this:

Code Block

label.greetings=Welcome!

By adding a model, we can use property expressions to interpolate properties from the model into the localized string in the resource bundle. For example, suppose we wanted to add the name from a User object to the greeting above. We'd say:

Code Block

User user;
...
add(new Label("greetings", new StringResourceModel("label.greetings", this, user)));

and have a resource like:

Code Block

label.greetings=Welcome, ${user.name}!

where User has a getName() method exposing its "name" property.

Detachable Models

If you look at the inheritance hierarchy for types that implement IModel in the Wicket core, you will notice that there are two root classes which implement IModel directly: AbstractModel and AbstractDetachableModel. AbstractModel is intended as a base class for all models which do not make use of IModel's IDetachable interface. To remove the need to implement IModel.IDetachable.detach, AbstractModel looks like this:

Code Block

public abstract class AbstractModel implements IModel
{
	public void detach()
	{
	}
}

AbstractModel has a single subclass, Model, which we talked about at the beginning of this article, which wraps a Serializable object.

All other model classes in Wicket are "detachable" and extend the base class AbstractDetachableModel. A detachable model in Wicket is a model which can get rid of a large portion of its state to reduce the amount of memory it takes up and to make it cheaper to serialize when replicating it in a clustered environment. When an object is in the detached state, it contains only some very minimal non-transient state such as an object id that can be used to reconstitute the object from a persistent data store. When a detached object is attached, some logic in the object uses this minimal state to reconstruct the full state of the object. This typically involves restoring fields from persistent storage using a database persistence technology such as JDO or Hibernate.

To make implementation of detachable models easy, AbstractDetachableModel provides some basic inheritable logic for attaching and detaching models. The basis for this logic is a transient boolean field which is true when the object is in the attached state and false when it is in the detached state:

Code Block

private transient boolean attached = false;

Which state a detachable object is in is available via:

Code Block

public boolean isAttached()
{
	return attached;
}

Two methods, attach() and detach() (which implements the IDetachable interface contract), use this boolean to call the abstract subclass methods onAttach() and onDetach() only when required:

Code Block

public final void attach()
{
	if (!attached)
	{
		attached = true;
		onAttach();
	}
}

public final void detach()
{
	if (attached)
	{
		attached = false;
		onDetach();
	}
}

protected abstract void onAttach();
protected abstract void onDetach();

The implementations of IModel.getObject(Component) and IModel.setObject(Component) automatically call attach() before delegating responsibility to abstract methods:

Code Block

public final Object getObject(final Component component)
{
	attach();
	return onGetObject(component);
}

public final void setObject(final Component component, final Object object)
{
	attach();
	onSetObject(component, object);
}

protected abstract Object onGetObject(final Component component);
protected abstract void onSetObject(final Component component, final Object object);

All of this very neatly encapsulates what it means to be a detachable object in Wicket. If you implement these abstract methods:

Code Block

public abstract Object getNestedModel();
protected abstract void onAttach();
protected abstract void onDetach();
protected abstract Object onGetObject(final Component component);
protected abstract void onSetObject(final Component component, final Object object);

you will immediately have a functioning detachable model that shrinks and then restores its state when replicated in a cluster.

There are two sub-types of AbstractDetachableModels. The first is AbstractPropertyModel and the second is AbstractReadOnlyDetachableModel. The latter is very simple and just adds this override to AbstractDetachableModel:

Code Block

protected final void onSetObject(final Component component, final Object object)
{
	throw new UnsupportedOperationException("Model " + getClass()
			+ " does not support setObject(Object)");
}

The StringResourceModel we talked about above is an AbstractReadOnlyDetachableModel since resource strings are read-only in nature.

AbstractPropertyModel provides implementation details for the PropertyModel, CompoundPropertyModel and BoundCompoundPropertyModel classes we discussed above by implementing onGetObject() and onSetObject() using a property expression and conversion type retrieved from a subclass via abstract methods propertyExpression() and propertyType(). The simple PropertyModel class just returns the property expression and type it was constructed with:

Code Block

protected String propertyExpression(Component component)
{
	return expression;
}

protected Class propertyType(Component component)
{
	return propertyType;
}

While CompoundPropertyModel returns the name of the component as its property expression:

Code Block

protected String propertyExpression(final Component component)
{
	return component.getId();
}

protected Class propertyType(final Component component)
{
	return null;
}

Finally, BoundCompoundPropertyModel stores a set of bindings internally and returns the property expression for a component based on this information:

Code Block

protected String propertyExpression(final Component component)
{
	final Binding binding = getBinding(component);
	return binding != null ? binding.propertyExpression : component.getId();
}

protected Class propertyType(final Component component)
{
	final Binding binding = getBinding(component);
	return binding != null ? binding.type : null;
}

which takes a model object and a property expression. When the property model is asked for its value by the framework, it will use the property expression to access the model object's property. For example, if we have a Java Bean or "POJO" (Plain Old Java Object) like this:

Code Block

class Person
{
  private String name;

  Person(String name)
  {
    this.name = name;
  }

  String getName()
  {
    return name;
  }
}

then the property expression "name" can be used to access the "name" property of any Person object via the getName() getter method.

Code Block

personForm.add(new RequiredTextField("personName", new PropertyModel(person, "name")));

Nested property expressions are possible as well. You can access sub-properties via reflection using a dotted path notation, which means the property expression "person.name" is equivalent to calling getPerson().getName() on the given model object.

A second constructor on PropertyModel is available for models that wish to access properties involving type conversions:

Code Block

public PropertyModel(final Object modelObject, final String expression, Class propertyType)

Although the property models will do automatic type conversions for you, it may not always do the kind of conversion you desire. This alternative constructor creates a property model that forces a particular conversion to occur. For example, you may wish to access a String or Object property of something as an Integer. Constructing a PropertyModel with new PropertyModel(object, "myString", Integer.class) would force the property to be converted to and from an Integer on get/set operations.

There are three principal reasons why you might use PropertyModel instead of Model:

  • PropertyModel instances are dynamic
  • the property expression language is more compact than the analogous Java code
  • it's much simpler to create a property model than to subclass Model

Compound Property Models

Compound models allow containers to share models with their children. This saves memory, but more importantly, it makes replication of models much cheaper in a clustered environment. The basic idea is that the contained components usually want model data that can be easily derived at runtime from the model of their container. So, give the contained components no explicit model, and when a model is needed, Wicket will search up the containment hierarchy for a compound model. The compound model can retrieve model data for any of its contained components.

CompoundPropertyModel is the most commonly used compound model. An instance of this class uses the name of the contained component as a property expression to retrieve data from its own model data.

To use a CompoundPropertyModel, simply set one as the model for a container, such as a Form or a Page. Create the contained components with no model of their own. Insure that the component identifier names match the appropriate property names.

Here's a simple example using a CompoundPropertyModel. Suppose we have a Person class, with two properties: Name and Age. We want a simple form for the user to edit a Person.

Code Block

Form personForm = new Form("aPersonForm", new CompoundPropertyModel(person));
personForm.add(new RequiredTextField("name"));
personForm.add(new RequiredTextField("age", Integer.class));

(A complete working example would require a save button and so forth but the use of a compound model doesn't change those.)

The component name can in fact be a more complicated property expression. Suppose for example that the Person class also has an address property, of class Address, and that class in turn has a city property. To define this field in the form we can do this:

Code Block

personForm.add(new RequiredTextField("address.city"));

The corresponding input field in the html must have a wicket id of "address.city". This works, but it does expose the internal structure of the model data in the html. CompoundPropertyModel has a method that can be used to rectify this.

The model associates a different property expression with the component being bound.

Code Block

public <S> IModel<S> bind(String property)

With this association in place the child component can have whatever name we like, rather than having the match the property expression.

To use CompoundPropertyModel.bind for the city field discussed above we might do something like this:

Code Block

CompoundPropertyModel personModel = new CompoundPropertyModel(person);
Form personForm = new Form("aPersonForm", personModel);
TextField cityField = new RequiredTextField("city", personModel.bind("address.city"));
personForm.add(cityField);

Also, note that if you are using a component that you do not want to reference the compound property model, but is a child of the form, that you define a model for that component. For example:

Code Block

// throws exception
personForm.add(new Label("non-compound-model-reference"));
// does not throw an exception
personForm.add(new Label("non-compound-model-reference", new Model()));

Wrapped Object Models

It is possible to create Models that explicitly define in normal java code what is to be returned as the model object for each property within the object being wrapped. So instead of specifying via a string the name of the property to fetch the value you from the specification is done in Java.

While creating Model's in this pattern takes longer (more classes) than using Reflection based PropertyModels it prevents the problems that can occur when critical functionality is defined in String based context that most IDE's do not refactor properly.

It also helps with readability when the models are added to components to be able to easily see the types involved.

These are the Address and Person classes used in the previous examples:

Code Block

public class Address
  implements Serializable
{

  private String city;

  public Address()
  {
    super();
  }

  public String getCity()
  {
    return city;
  }

  public void setCity(String city)
  {
    this.city = city;
  }

}


public class Person
  implements Serializable
{

  private String name;
  private int age;

  private Address address;

  public Person()
  {
    super();
  }

  public String getName()
  {
    return name;
  }

  public void setName(String name)
  {
    this.name = name;
  }

  public int getAge()
  {
    return age;
  }

  public void setAge(int age)
  {
    this.age = age;
  }

  public Address getAddress()
  {
    return address;
  }

  public void setAddress(Address address)
  {
    this.address = address;
  }

}

The first step is to create a Wrapped Object Model for the Address and Person classes:

Code Block

public class AddressModel
  implements IModel
{

  private IModel addressContainingModel;

  private AddressModelType type;

  public enum AddressModelType
  {
    CITY_MODEL;
  };

  public AddressModel(IModel addressContainingModel, AddressModelType type)
  {
    this.addressContainingModel = addressContainingModel;
    this.type = type;
  }

  @Override
  public Object getObject()
  {

    Address address = (Address) addressContainingModel.getObject();

    switch (type)
    {

      case CITY_MODEL:

        return address.getCity();
    }

    throw new UnsupportedOperationException("invalid AddressModelType = "
                                            + type.name());
  }

  @Override
  public void setObject(Object object)
  {

    Address address = (Address) addressContainingModel.getObject();

    switch (type)
    {

      case CITY_MODEL:

        address.setCity((String) object);
        break;
      default:
        throw new UnsupportedOperationException("invalid AddressModelType = "
                                                + type.name());
    }

  }

  @Override
  public void detach()
  {

    addressContainingModel.detach();

  }

}


public class PersonModel
  implements IModel
{

  private IModel personContainingModel;
  private PersonModelType type;

  public enum PersonModelType
  {
    NAME_MODEL, AGE_MODEL, ADDRESS_MODEL;
  }

  public PersonModel(IModel personContainingModel, PersonModelType type)
  {
    this.personContainingModel = personContainingModel;
    this.type = type;
  }

  @Override
  public Object getObject()
  {
    Person person = (Person) personContainingModel.getObject();

    switch (type)
    {

      case NAME_MODEL:

        return person.getName();

      case AGE_MODEL:
        return new Integer(person.getAge());

      case ADDRESS_MODEL:
        return person.getAddress();

    }

    throw new UnsupportedOperationException("invalid PersonModelType = "
                                            + type.name());
  }

  @Override
  public void setObject(Object object)
  {

    Person person = (Person) personContainingModel.getObject();

    switch (type)
    {

      case NAME_MODEL:

        person.setName((String) object);
        break;

      case AGE_MODEL:
        person.setAge((Integer) object);
        break;

      case ADDRESS_MODEL:
        person.setAddress((Address) object);
        break;

      default:
        throw new UnsupportedOperationException("invalid PersonModelType = "
                                                + type.name());
    }

  }

  @Override
  public void detach()
  {
    personContainingModel.detach();
  }

}

Notice how each wrapped model contains an inner model that contains the actual pojo instance. This allows for the wrapped model to be a plain Model or a LoadableDetachableModel, or even another wrapped model where its .getObject() results in a suitably typed input value (see the "address.city" field in the example below).

At this point to create a form using our wrapped object models looks like:

Code Block

Person p = new Person ();

Form personForm = new Form("aPersonForm");
personForm.add(new RequiredTextField("name", new PersonModel (new Model (p), PersonModelType.NAME_MODEL)));
personForm.add(new RequiredTextField("age", new PersonModel (new Model (p), PersonModelType.AGE_MODEL), Integer.class));
personForm.add(new RequiredTextField("address.city", new AddressModel (new PersonModel (new Model (p),
                                        PersonModelType.ADDRESS_MODEL), AddressModelType.CITY_MODEL)));
add(personForm);

A wrapped object model also makes working with DataTables's easier as one IColumn implementation can be written for each object class which makes the declaration of the table much simpler.

e.g.

Code Block

public class PersonTableColumn
  extends AbstractColumn
{
  private final PersonModelType type;

  public PersonTableColumn(String columnName, PersonModelType type)
  {
    super(new Model(columnName));
    this.type = type;
  }

  public void populateItem(Item cellItem, String componentId, IModel rowModel)
  {

    switch (type)
    {
      // this needs to be handled seperately due to it being an Address
      // instance from the Person object.
      case ADDRESS_MODEL:
        cellItem
          .add(new Label(componentId,
                         new AddressModel(new PersonModel(rowModel,
                                                          PersonModelType.ADDRESS_MODEL),
                                          AddressModelType.CITY_MODEL)));
        break;

      default:
        cellItem.add(new Label(componentId, new PersonModel(rowModel, type)));
    }
  }
}

So the table could be declared like:

Code Block

DataTable table = new DataTable("datatable", new IColumn[] {
      new PersonTableColumn("Name", PersonModelType.NAME_MODEL),
      new PersonTableColumn("Age", PersonModelType.AGE_MODEL),
      new PersonTableColumn("City", PersonModelType.ADDRESS_MODEL)
  }, new PersonProvider(), 10);

Another option with the complex object is to create a custom IConverter that will take in this case the Address instance from the PersonModel and render the string value as the city name.

e.g.

Code Block

public class CustomLabel extends Label {

	private final IConverter converter;

	/**
	 * @param id
	 * @param label
	 */
	public CustomLabel(String id, IModel labelModel, IConverter converter) {
		super(id, labelModel);
		this.converter = converter;
	}

	/* (non-Javadoc)
	 * @see org.apache.wicket.Component#getConverter(java.lang.Class)
	 */
	@Override
	public IConverter getConverter(Class type) {

		return this.converter;
	}


}

With the populate from above as:

Code Block

  public void populateItem(Item cellItem, String componentId, IModel rowModel)
  {

    switch (type)
    {

      case ADDRESS_MODEL:
        cellItem
          .add(new CustomLabel(componentId,
                               new PersonModel(rowModel,
                                               PersonModelType.ADDRESS_MODEL),
                               new IConverter()
                               {
                                 public Object convertToObject(String arg0,
                                                               Locale arg1)
                                 {
                                   throw new UnsupportedOperationException("converter is readonly.");
                                 }

                                 public String convertToString(Object object,
                                                               Locale locale)
                                 {
                                   Address address = (Address) object;
                                   return address.getCity();
                                 }
                               }));
        break;

      default:
        cellItem.add(new Label(componentId, new PersonModel(rowModel, type)));
    }

  }

Resource Models

ResourceModel

Localization of resources in Wicket is supported by the Localizer class. This provides a very convenient way to retrieve and format application-wide or component-specific resources according to the Locale that the user's Session is running under. You can retrieve a String using Localizer and use the result as a model object, but it is usually more convenient to use one of Wicket's resource model classes.

ResourceModel is the simplest of these. It simply takes a resource key and uses it to determine the model object (by looking for the resource in a property file). Thus for a example a label can be created this way:

Code Block

add(new Label("greetings", new ResourceModel("label.greetings")));

(Note however that you can also accomplish the same thing using the wicket:message tag in the html.)

ResourceModel has another constructor which takes an additional string to be used as a default if the resource is not found.

The model objects created via ResourceModel vary according to the locale and the contents of the resource bundle, but they don't otherwise depend on the program state. For example, the greeting message produced above doesn't contain the user's name. More dynamic models can be constructed using StringResourceModel.

Note that the ResourceModel, like other models that use the ComponentStringResourceLoader, takes two attempts to load each key it's looking for. I.e., in addition to the various style/locale/component-path retries, it looks using both a relative path and an absolute path. To try & clarify using the example above, it will look for both the property "greetings.label.greetings" (assuming the Label is directly added to the page) as well as the absolute "label.greetings" key. If the label was part of a form, it could be "formName.greetings.label.greetings" (etc...).

StringResourceModel

StringResourceModels have a resource key, a Component, an optional model and an optional array of parameters that can be passed to the Java MessageFormat facility. The constructors look like this:

Code Block

public StringResourceModel(final String resourceKey, final Component component, final IModel model)
public StringResourceModel(final String resourceKey, final Component component, final IModel model, final Object[] parameters)

The resourceKey is used to find the resource in a property file. The property file used depends on the component.

A very simple example of a StringResourceModel might be a Label constructed like this:

Code Block

add(new Label("greetings", new StringResourceModel("label.greetings", this, null)));

where the resource bundle for the page contains an entry like this:

Code Block

label.greetings=Welcome!

This label is essentially the same as that constructed with ResourceModel above. However, with StringResourceModel we can add the name from a User object to the greeting. We pass the user object as the model parameter to the constructor. Wicket will find the localized string in the resource bundle, find any substrings within it of the form ${propertyexpression}, and replace these substrings with their values as property expressions. The model is used to evaluate the property expressions.

For example, suppose we wanted to add the name from a User object to the greeting above. We'd say:

Code Block

User user;
...
add(new Label("greetings", new StringResourceModel("label.greetings", this, new Model(user))));

and have a resource like:

Code Block

label.greetings=Welcome, ${name}!

where User has a getName() method exposing its "name" property.

Note that StringResourceModel is dynamic: the property expression is re-evaluated every time getObject() is called. In contrast, if the application code called Localizer itself and used the resulting string as model data, the result would be a static (non-changing) model.

Detachable Models

At the end of each request cycle the active page is serialized into a byte array and stored for the next request (some in the session and some to disk: see IPageStore for more info). The entire object graph of the page including all components and their associated models are included in generated byte stream (byte array). Once you get past all but the most trivial of applications this object graph becomes huge and the time required to serialize it becomes a bottle neck. Keeping the size of the serialized pages low will increase the number of concurrent users your application can support. It will also minimize the amount of time required to fail over to another cluster node and the bandwidth to keep the sessions replicated.

The Wicket solution to this problem is to create the concept of detachability where by at the end of the request cycle all the components contained in a page are recursively detached (See Component.detatch()) which dramatically reduces the size of the object graph being serialized. Each component in turn then detaches its internal state including the value of its default model by calling the IModel.detach() method.

So a Detachable Model is an IModel that releases most of its object graph when you call detatch() retaining just enough detail (typically a unique identifier) that can be used to reconstitute the object on the next call to getObject() which will occur on the next page render.

Two commonly used built-in Wicket IModel implementations are:

  1. AbstractReadOnlyModel
  2. LoadableDetachableModel (also commonly referred to as an LDM)

It is also possible to create a custom IModel implementation that mirrors the LoadableDetchableModel inner workings but also supporting IModel.setObject().

Managing the lifecycle of non default detachable IModel's

Wicket provides the detachability hooks and will automatically detach the default model of each component. However its common to use more than a single IModel implementation per component especially when using nested or chaining models. In this case the developer is responsible for registering their custom models to participate in the detachment process.

The best way to handle this registration is to make sure it occurs in the same scope as where the IModel implementation was instantiated.

For example:

Code Block


public class MyPanel extends Panel {

   private final DetatchableModelA detachableModelA;


   public MyPanel (String id) {
       super(id);

       this.detachableModelA = new DetachableModelA();

       add (new CustomFormPanel ("formPanel", new CListModelFromA (this.detachableModelA)));

   }

   @Override
   public void detach() {

     // remember to call the parent detach logic
     super.detach();

     this.detachableModelA.detach();

   }
}

Here you see that the DetachableModelA instance is created in the MyPanel constructor and then passed in as the inner model to the CListModelFromA model which will load a List<C> instances based on the details of the A instance contained in the model. Because the this.detachableModelA instance could possibly be shared like this in many sub component models its important that the CListModelFromA implementation does not try and call detach() on it but rather leave the detachment to occur in the same scope that the model was instantiated. In this case the detachment is done in the MyPanel.detach.

AbstractReadOnlyModel

When you subclass AbstractReadOnlyModel you implement the:

Code Block

public abstract T getObject();

So long as your subclass does not contain any private fields then no business data will be serialized when the the request cycle ends. The model object value is computed
at the time the method is called and not cached anywhere.

For example:

Code Block

public class PersonNameModel extends AbstractReadOnlyModel<String> {

    private final IModel<Person>personModel;

    public PersonNameModel (IModel<Person>personContainingModel> personModel) {
        this.personModel = personModel;
    }

    public String getObject() {
        Person p = this.personModel.getObject();

        if (p == null)
            return "";

        return p.getName();
    }

}

You can see it takes in an existing IModel that contains the Person object and that the getObject() method extracts the name of the person.

LoadableDetachableModel

This class extends AbstractReadOnlyModel and finalizes the IModel.getObject() method requiring you to implement:

Code Block

protected abstract T load();

This method is called once per request cycle, the first time that the getObject() method is invoked. The T object value is then cached in a transient private field and used as the return value to all subsequent calls to getObject().

For example:

Code Block

class LoadablePersonModel extends LoadableDetachableModel<Person> {

	Long id;

	LoadablePersonModel(Long id)	 {
		this.id = id;
	}

	@Override
	protected Person load() {
		return DataManager.getPersonFromDatabase(id);
	}
}

When getObject() is called for the first time on a given instance of this class, it will call load() to retrieve the data. At this point the data is said to be attached. Subsequent calls to getObject() will return the attached data. At some point (after the page has been rendered) this data will be removed (the internal references to it will be set to null). The data is now detached. If getObject() is subsequently called (for example, if the page is rendered again), the data will be attached again, and the cycle starts anew.

Read/Write Detachable Model

Here is an example of how to create a model that is loadable and detachable but also supports using the IModel.setObject(T value) to store a different value into the model.

Code Block

public class LoadableDetachableAttributeModel implements
		IModel<Attribute> {


	private transient Attribute cachedAttribute = null;
        private transient boolean attached = false;
	
	private String attributeName = null;

        private final AttributeService service;

	public LoadableDetachableAttributeModel (AttributeService service) {
            this.service = service;
        }
    

	public LoadableDetachableAttributeModel (AttributeService service, Attribute defaultValue) {
	      this (service);
              if (defaultValue != null) {
                   setObject(defaultValue);
              }
	}
	
	/* (non-Javadoc)
	 * @see org.apache.wicket.model.IDetachable#detach()
	 */
	@Override
	public void detach() {

                attached = false;
		cachedAttribute = null;
	}

	/* (non-Javadoc)
	 * @see org.apache.wicket.model.IModel#getObject()
	 */
	@Override
	public Attribute getObject() {

                if (!attached) {
                
                    // load the attribute
                    attached = true;
                    
                    if (attributeName != null) {

                       // load the attribute from the service
                       this.cachedAttribute = service.loadAttribute (this.attributeName);
                    }
                }
                return this.cachedAttribute;

	}

	/* (non-Javadoc)
	 * @see org.apache.wicket.model.IModel#setObject(java.lang.Object)
	 */
	@Override
	public void setObject(Attribute object) {

                this.attached = true;

		if (object == null) {
		      this.attributeName = null;
                      this.cachedAttribute = null;			
		}
		else {
			attributeName = object.getName();
                        this.cachedAttribute = object;
		}		
		
	}
	
	

Chaining models

Suppose you want a model that is both a loadable model and a compound model, or some other combination of the model types described above. One way to achieve this is to chain models, like this:

Code Block

CompoundPropertyModel personModel = new CompoundPropertyModel(new LoadablePersonModel(personId));

(here LoadablePersonModel is a subclass of CompoundPropertyModel, as described earlier)

The model classes in the core Wicket distribution support chaining where it make sense.

More about the IModel interface

We are now in a position to understand the complete IModel interface:

Code Block

public interface IModel extends IDetachable
{
	public Object getNestedModel();
	public Object getObject(final Component component);
	public void setObject(final Component component, final Object object);
}

IModel extends IDetachable, which means that all models must provide a method public void detach(). Some model classes (Model for one) provide an empty, no-op implementation.

In some cases model instances form a natural hierarchy. For example, several CompoundPropertyModels share the same model object. The getNestedModel() method will return this common shared object. In cases where there is no such object it returns null.

Compound models are also the motivation for the component parameters to getObject and setObject. Several components may share the same CompoundPropertyModel object. If getObject (or setObject) is called it must return something different depending on the component (e.g., evaluate the appropriate property expression). Thus these two methods must be passed a component as a parameter.

Panel

The IModel interface was simplified in Wicket 2.0:

Code Block

public interface IModel<T> extends IDetachable
{
  T getObject();
  void setObject(final T object);
}

The get and set methods do not take a component argument anymore. Instead, Wicket 2.0 has specialized model interfaces to do with specific issues like recording the 'owning' component of a model. See IAssignmentAwareModel and IInheritableModel (though you typically don't need to know these interfaces directly).

Another change is that IModel does now support generics. This is especially interesting when authoring custom components where you allow only models (compile time) that produce a certain type. ListView for instance only accepts models that produces instances of java.util.List.

Refactor Safe Property Models

Annotation processors

There are a number of annotation processors that generate a meta data that can be used to build safe property models. Examples of such processors:

LambdaJ

With a little bit of help from the LambdaJ project we can stop using fragile PropertyModels.

Code Block

/* www.starjar.com
 * Copyright (c) 2011 Peter Henderson. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0
 */
package com.starjar.wicket;

import ch.lambdaj.function.argument.Argument;
import ch.lambdaj.function.argument.ArgumentsFactory;
import org.apache.wicket.model.PropertyModel;

public class ModelFactory {

  /**
   * Use with the on() function from Lambdaj to have refactor safe property models.
   *
   * e.g.
   * <pre>
   * import static com.starjar.wicket.ModelFactory.*;
   * import static ch.lambdaj.Lambda.*;
   *
   * Contact contact = getContactFromDB();
   *
   * Label l = new Label("id", model(contact, on(Contact.class).getFirstName()));
   *
   *
   * </pre>
   *
   * OR
   *
   * <pre>
   * import static com.starjar.wicket.ModelFactory.*;
   * import static ch.lambdaj.Lambda.*;
   *
   * ContactLDM contactLDM = new ContactLDM(contactId);
   *
   * Label l = new Label("id", model(contactLDM, on(Contact.class).getFirstName()));
   * </pre>
   *
   * Works as expected for nested objects
   *
   * <pre>
   * Label l = new Label("address", model(contactLDM, on(Contact.class).getAddress().getLine1()));
   * </pre>
   *
   *
   * @param <T> Type of the model value
   * @param value
   * @param proxiedValue
   * @return
   */
  public static <T> PropertyModel<T> model(Object value, T proxiedValue) {
    Argument<T> a = ArgumentsFactory.actualArgument(proxiedValue);
    String invokedPN = a.getInkvokedPropertyName();
    PropertyModel<T> m = new PropertyModel<T>(value, invokedPN);
    return m;
  }
}

Which can then be used

Code Block

import static ch.lambdaj.Lambda.*;
import static com.starjar.wicket.ModelFactory.*;


public class MyPanel extends Panel {
  public MyPanel(String id) {
    Label l = new Label("firstName", model(contact, on(Contact.class).getFirstName());
    add(l);
    Label addr = new Label("address", model(contact, on(Contact.class).getAddress().getLine1());
    add(addr);
  }
}

...