Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

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 AbstractDetachableModelhave the ability to be detachable. 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.

If you look again at the IModel, you will notice that this interface extends IDetachable interface, which has single method void detach(). The simplest IModel implementation, Model implements this method as a no-op. However, there are other models, which provide true detachable functionality.

To make implementation of detachable models easy, AbstractDetachableModel LoadableDetachableModel 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
public boolean isAttached()
{
  return attached;
}

Two methods, attach() and This boolean variable is used to determine whether to load the transient object from the persistence technology. Furthermore, it is used in the detach() method (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 attachdetach()
{
  if (!attached)
  {
    attached = truefalse;
    transientModelObject = null;
    onAttachonDetach();
  }
}

public final void detachprotected void onAttach()
{
}

protected void onDetach()
{
}

The transient object is loaded in the getObject() method, if necessary.

Code Block

public Object getObject()
{
    if (!attached)
    {
      attached = falsetrue;
    onDetach  transientModelObject = load();

   }
}

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);
    }
    return transientModelObject;
}

LoadableDetachableModel

Put in another words, LoadableDetachableModel holds a temporary, transient model object, that is set on attaching by calling abstract method load(), and that will be reset/set to null on detaching.

In the following example, we load objects (persons) on attachement.

Code Block

LoadableDetachableModel personListModel = new LoadableDetachableModel()
{
  protected Object load()
  {
     return getPersonDao().findPersons();
  }
};

During the handling of the request cycle, the list of persons we found by calling findPersons() is returned when getObject() is called on the personListModel. When detached, the model representation is cleaned up.

Another example would be storing a single object which can be reconstituted by its primary key.

Code Block

public class PersonModel extends LoadableDetachableModel
{
  private Integer primaryKey;
  
  public PersonModel(Integer primaryKey)
  {
    this.primaryKey = primaryKey;
  }

  protected Object load()
  {
     return getPersonDao().findPersonByPk(primaryKey);
  }
}

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

Read-only models

AbstractReadOnlyModel is another simple implementation of IModel. It has empty logic for detachment and disallows setting an model object, thus becoming read-only model.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
protectedpublic final void onSetObjectsetObject(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 another fine example of loadable detachable (and therefore read-only) model, since resource strings are read-only in nature. Actually, the StringResourceModel is a subclass of LoadableDetachableModel.

Property models

The first example of property model is AbstractPropertyModel. It provides implementation details for the PropertyModel, CompoundPropertyModel and BoundCompoundPropertyModel classes we discussed above by implementing onGetObjectgetObject() and
onSetObjectsetObject() using a property expression and conversion type retrieved from a subclass via abstract methods method 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;
}

The conversion type is determined in the method getObjectClass() provided by AbstractPropertyModel.

Another type of property model, CompoundPropertyModel, 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  if (binding != null ? binding.propertyExpression : component.getId();
}

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

LoadableDetachableModel

A model that makes working with custom detachable models a breeze is LoadableDetachableModel. LoadableDetachableModel holds a temporary, transient model object, that is set on attaching by calling abstract method 'load', and that will be reset/set to null on detaching.

In the following example, we load objects (venues) on attachement.

Code Block

LoadableDetachableModel venueListModel = new LoadableDetachableModel()
{
  protected Object load(.propertyExpression;
  }
  else if (component != null)
  {
     return getVenueDao()component.findVenuesgetId();
  }
  return null;
};

...

Special purpose models

Some components need or use specialized models for their workings. Or, as is the case with components that inherit from AbstractChoice, they need more than one model. Currently, these specialized models are IChoiceList ListItemModel and FileUpload. You can find information on them in the JavaDocs of the components that use them, or you can take a look at the examples.