Wicket, at the end of each request cycle, serializes the active page into a byte array and stores it for the next request. The entire object graph of the page including all components and their associated models are included in the generated byte stream. This could possibly, depending on the size of the graph, place stress on server resources.
So how can we reduce our page footprint and put our applications on a diet? By mastering Wicket models, that’s how. As it turns out, understanding static, dynamic and loadable/detachable models is key if you want to reduce the memory footprint of your Wicket applications.
To begin out discussion of Wicket’s models let’s first look at the IModel interface.
IModel
All Wicket models implement the IModel interface which is defined as follows:
public interface IModel<T> extends IDetachable { /** * Gets the model object. * * @return The model object */ T getObject(); /** * Sets the model object. * * @param object * The model object */ void setObject(final T object); }
The IModel interface defines 2 methods:
- getObject is called by Wicket when rendering the component.
- setObject is called by Wicket when it has to update the component such as after all form components have validated (see Wicket request life cycle and form validation).
IDetachable
In addition to defining the getObject and setObject methods, IModel also extends the IDetachable interface which is defined as follows:
public interface IDetachable extends IClusterable { /** * Detaches model after use. This is generally used to null out transient references that can be * re-attached later. */ void detach(); }
The IDetachable interface defines only 1 method:
- detach is called by Wicket at the end of a request’s lifecycle.
IModel and IDetachable form the foundation of Wicket’s model API which allows for both static and dynamic model implementations. The 4 primary model implementations that Wicket provides are:
- Model – the simplest of all Wicket models. It can be used as both a static and as a dynamic model.
- PropertyModel – a dynamic model.
- CompoundPropertyModel – a dynamic model that also provides field binding.
- LoadableDetachableModel – a dynamic model that detaches the value it wraps at the end of a request’s lifecycle.
Static And Dynamic Models
Here’s an example of using Model as a static model:
Person person = Person.getPerson();
add(new Label("firstName", new Model<String>(person.getFirstName())));
add(new Label("lastName", new Model<String>(person.getLastName())));
And here’s an example of using Model as a dynamic model:
Person person = Person.getPerson(); add(new Label("firstName", new Model<String>(){ @Override public String getObject() { return Person.getPerson().getFirstName(); } })); add(new Label("lastName", new Model<String>(){ @Override public String getObject() { return Person.getPerson().getLastName(); } }));
In the above code for both static and dynamic models we are adding 2 Label components to the page. The Label component constructor takes as a second parameter a reference to a Model object.
In our static example above notice how each of the models directly wraps its value. Each of the models holds a reference to a String and in Java strings are immutable; even if the Person object’s firstName and lastName field values change and if the page were refreshed the values displayed in the two label components will always show the values they were created with. This is the reason these types of models are called static.In our dynamic example above notice how instead of using Wicket’s basic Model class we subclass Model and override its getObject method (see IModel) to return the appropriate value. Whenever Wicket needs to render the above components it will call its model’s getObject methods to obtain the values to be rendered; now, if the Person object’s firstName and lastName field values change and if the page were refreshed the values displayed in the two label components will reflect those changes. This is the reason these types of models are called dynamic.
So let me reiterate here by giving you a concise definition of static and dynamic models:
- A static model is any model that directly wraps a value.
- A dynamic model is any model that does not directly wrap a value but instead overrides the getObject method to retrieve the value when Wicket renders the component.
In the above and for the remainder of this article I use the word ‘value’ to denote either a primitive or a reference value.
Some Models Are Heavier Than Others
What makes Wicket a stateful framework is the serialization of its components at the end of each request’s life cycle. However, this doesn’t come without a price which is the amount of memory that can be consumed by serializing the values that component models wrap. While a page with 2 Labels might seem trivial, scale that out to thousands or tens of thousands of users concurrently hitting this page and it isn’t so trivial anymore.
So how can we manage the memory footprint of our pages to significantly reduce the load that our models have on memory? The answer is by using LoadableDetachableModels. At the beginning of this article I introduced the IDetachable interface and this little nugget is the key to Wicket’s LoadableDetachableModel.
Lets consider a slightly more involved use case that involves a form that uses a CompoundPropertyModel:
Form<Person> form = new Form<Person>("form", new CompoundPropertyModel<Person>(Person.getPerson()));
form.add(new TextField<String>("firstName"));
form.add(new TextField<String>("lastName"));
add(form);
In this example we are creating a form with a CompoundPropertyModel of type Person. When this page is serialized the Person instance that the CompoundPropertyModel wraps will be serialized. That is, unless we take steps to prevent it from serializing Person.
This use case thus presents us with a unique challenge; how can we use a CompoundPropertyModel without incurring the load on memory caused by serializing the Person instance? The answer lies in Wicket’s LoadableDetachableModel and Wicket’s ability to use nested models.
LoadableDetachableModel Take The Weight Off
LoadableDetachableModel is a model that loads and detaches the value it wraps by storing the reference to the value in a temporary, transient reference which it sets when its getObject method is called by calling the abstract method ‘load’ and which it sets to null when the method ‘detach’ is called.
LoadableDetachableModel ldm = new LoadableDetachableModel<Person>() { @Override protected Person load() { return Person.getPerson(); } };
As you can see from the above example we override the load method to return an instance of Person. In a real application we could query a database.
Using LoadableDetachableModel allows us to attach a reference to Person on each request and detach it at the end of the request cycle thereby eliminating the need to serialize the Person object.
[*** In addition, another added benefit you get from using a detachable model is when you are dealing with objects that for one reason or another can’t be serialized. In those cases you can just wrap them in a detachable model thereby eliminating the need to serialize them. ***]
Now we know we can use LoadableDetachableModel when we want to dynamically load and discard the values wrapped by our models. But how can we use them and still take advantage of the binding features that the CompoundPropertyModel provides? Nesting models is the answer.
Nesting Models
Form<Person> form = new Form<Person>("form", new CompoundPropertyModel<Person>(ldm));
form.add(new TextField<String>("firstName"));
form.add(new TextField<String>("lastName"));
add(form);
Notice in the above example that we are passing our LoadableDetachableModel instance, ldm, as a parameter to the CompoundPropertyModel’s constructor. By nesting a LoadableDetachableModel in a CompoundPropertyModel we get the benefit of both models. That’s like having your cake and eating it, too!
In my opinion nested models are one of the coolest features that Wicket provides - they reduce the load on memory that Wicket’s stateful nature would have had we not been able to eliminate serializing the values wrapped by its models.
So, the next time someone tells you that scaling out a Wicket application is difficult because it serializes its pages just don’t believe it; Wicket makes it incredibly easy by using nested models.
Well that concludes my little discussion of Wicket models and I hope you have enjoyed it. Remember, having a solid understanding of models is a must if you seriously want to be a master Wicket programmer.
it was a very good explanation.. really awesome. Thanks.
ReplyDelete