This New User's Guide is a work in progress on a manual for Wicket. Using a wiki is a nice form for collaborative work on documentation.
To build a wicket web application you need to understand the WAR-file layout and what the
WEB-INF/lib is all about, otherwise you should go read the Introduction on Java web applications.
The following JAR-files are the bare minimum needed in
WEB-INF/lib to develop a wicket application:
The use of Servlet filters in the
web.xml configuration is explained here.
That's it, now go kick the tires!
Eclipse, NetBeans, IntelliJ
There are descriptions at the Wicket Quickstart on how to set up your favourite development environment.
qwicket can create a ready made project for you.
My First Application
The Wicket application we are going to develop is the ubiquitous Hello, World! application. Simply put, we will create a web page using Wicket technology for printing the Hello, World! string on a web page.
In the second release, some interactivity will be added to show how to do simple form related component development.
The goal is to generate the following markup:
Even though the requested result is so simple, there are many ways to achieve it. In this example we will use a rendering component called
'Label' to create the text 'Hello, World!'. So basically we have two things to consider:
- the HTML page
- the label component
The markup for the HTML page is the same as earlier:
Using this HTML-code is cheating, since we don't generate the required string programatically, but have hard coded it in the HTML page.
In order to have Wicket render the text through a component, we need to tell Wicket which piece of the web page is a component. In this case we can replace the text with a label component definition.
We have used the
span element to markup the component and the
wicket:id attribute to identify the component.
What we have accomplished until now is not enough to show the message in a web page. We have only told Wicket where to render the label component in the web page. In the next section we will show how to tell Wicket to render the message.
The Java Code
In Wicket creating a web page consists of two tasks: creating the markup in which the components are defined and other information is shown, and creating Java classes to couple the web page to the required functionality. In the case of this example, we need to tell Wicket it needs to render the Label component. We also need to tell the label component to render the text 'Hello, World!'. In this section we will perform these steps one at a time.
First we need to create a web page. Wicket has a
WebPage class suited for this task. All webpages need to be subclasses of
WebPage. Another requirement is that the actual HTML file and the class name are equal:
HelloWorld. They also need to be in the same place on the classpath. The best way to develop is to put them in the same directory. This might seem strange in the beginning, especially when you are accustomed to separate html files and java files. However, since all pages are actually just components, it makes perfect sense in terms of reusability.
Note that the base package names changed or rather moved from Wicket version 1.3. This means that if you are using these versions you would need to change your imports (and other references to these for packages/classes) to reflect these new package locations. The basic idea is just to prefix all your wicket packages with "org.apache". For example, the imports:
would change to:
When you add components, you need to give them an identifier in the form of a string. In the above example, our component's identifier is 'message'. The id should be unique within it's own scope. In other words, no siblings are allowed with the same id.
When the markup is rendered, the id's of tags are matched against the component ids. Thus
will look for a component with the id 'message' in the current scope.
Besides having an id, components also have a model. A model contains and represents a state of component. Usually, a model wraps an arbitrary object and provides data from this object to the component, which displays them. In this case, the model wrapes the string "Hello World!" and this string is used for the replacement of the body of the
span tag. Generally, you have to create an instance of model and associate it with the component (models are classes that implement the
IModel interface). However, many components have convenience constructors that construct a proper model for you. With labels, you can just provide a string, which is then wrapped into a simple model for the label to use.
Generally, you use models for the 'flexible' part of components, while it is best practice to keep your components immutable.
One more thing we need to get our example running is an Application object. You only need one for an application, and it can serve as a place to do the configuration for the application.
Our application could look like this:
Sidenote: To see changes to template files immediately, include the following snippet in your web.xml or to use a system property
To load our application and have it available, we need to add the following lines to web.xml:
And you are now ready to run your first Wicket app!
NOTE: Remember to change the wicket pages names to reflect the
org.apache base package for Wicket version 1.3 and up. See the 'The Java Code' section above.
Deploying the application
One easy way of testing the application is by using Wicket QuickStart. Note that in some releases, some jar files from the jetty distribution were omitted: you need to add
ext/jasper-*.jar to your buildpath manually.
If you wish to build the package manually, you would need the following directory structure.
Then to build the application, make sure
wicket-1.3.0.jar is in your classpath, and build the .java files. To run you need to ensure all .jar files in the "lib" directory are in your classpath. For most web servers and servlet containers (Tomcat for one) will automatically load all these files in the lib directory into your classpath.
It is recommended that you use the correct tools/methods to build your applications. The above can be used as an indication as to what is needed to be able to build/run a Wicket application. This information can be used to setup your project in your favorite environment (Eclipse, Netbeans, etc.) and server (Tomcat, etc.).
An interactive HelloWorld
The next thing we want to see is some interaction. Let us make the first example a little bit more dynamic.
We are going to extend our example to display an interactive message. We want the user to input what message should be displayed, and use that input to render our label with.
In the above markup you can see three Wicket components referenced:
'messageInput'. So, besides a label, we now have a form and input field.
The Java code
The first thing you should notice is the nesting of components. The
MessageForm is a sibling of the label, so it has to have a different id. The text field is a child of the
MessageForm and thus has a different scoping. As the text field is set as a child of the form, it needs to be a nested tag in the form as well.
The next thing to notice is the use of a
Model object. As said earlier, every Wicket component has a model. What a component uses its model for, depends on the component. A label uses its model to render the tag body, but a
ListView expects a
java.util.List as the model object, and loops through its elements. A model can be anything, as long as it implements
To process user input, you can use a
Form. Forms update the models of their nested
FormComponent-s automatically. It does this by calling
setObject(...) on the object with the input for that specific component. Hence, when a user pushes the submit button, the model of the text field is updated with the value that was in the HTML input text field. As, in this example, the label and the text field share the same model, when the model is updated via the text field, the label will render the new model object value as well.
More on models
Let us take a closer look at models.
The HelloWorld example program demonstrates the simplest model type in Wicket:
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:
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 constructor is merely there for convenience - it wraps its String argument in a Model object like this:
If we look up the inheritance hierarchy for Label, we see that
WebComponent, which in turn extends
Component. Each of these superclasses has only two constructors. One
Component constructor takes a String id:
And the other takes a
String id and an
IModel is the interface that must be implemented by all Wicket model objects. It looks like this:
And the base interface IDetachable looks like this:
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 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:
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 later.
Working with POJOs
While using a String like we did in the above example (
'Hello World') is convenient, you will usually want to work with domain objects directly. To illustrate how Wicket supports working with domain object, we use the following Plain Old Java Object (POJO) for our examples:
Like we have seen above, the simplest way of providing e.g. a
Label a model, is just to provide a string, and let the
Label implicitly construct one for you.
We can create the model explicitly like:
This has the same effect as the first example, but as we now have the reference to the model, we can use it e.g. to share it with other components like we did in our little interactive example earlier.
Using models like this works fine as long as there are no changes in the underlying data that should be displayed. You could call these models static or push models, as you have to synchronize any data changes to the model itself.
While static models are the easiest to use, it is often a good idea to let your models get their current objects dynamically from some source so that you can be certain that they display the most recent value. You could call these models dynamic or pull models.
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 an expression language which has a syntax similar to OGNL (see http://www.ognl.org) (until version 1.1, Wicket actually did use the OGNL library, however it has been replaced by custom implementation). The simplest
PropertyModel constructor is:
which takes a model object and a property expression. When the property model is asked for its value by the framework, it will use this property expression to access the model object's property.
Looking at our example POJO, the property expression
"name" can be used to access the
"name" property of any Person object via the
getName() getter method. Also, when updating a model, using property expression
"name" has the effect of calling
setName(String). Hence construct a property model to use our POJO, give it a property expression
"name" and set it on a
TextField, not only will that
TextField display the current value of the
"name" property, it will also update the
"name" property with any user input provided.
More complex property expressions are possible as well. For example, 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. Arrays can be accessed as in "
persons.name". As you can see, PropertyModels are quite a powerful way to give Wicket components access to model objects.
More information on the syntax
More information on the allowed property expressions can be found either in the
wicket.util.lang.PropertyResolver JavaDoc or on the Property Expression Language wiki page
Compound property models
If you have looked at some examples of Wicket already, you may have wondered why components, which generally must have a model, can be constructed with only an id, omitting any model. There are three reasons for this.
The first, most obvious reason is that not all components actually need a model. Panels and pages, for example, do not use models, though you might find it handy to store a model in these components.
The second 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
The third 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 particularly convenient and efficient lazy model initialization strategy is employed by the
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
Page. This is natural, because the compound model itself is usually the model that is being displayed by the
Page or edited by the
To see a
CompoundPropertyModel in action, take a look at the FormInput example . In the
InputForm nested class in
Form is constructed like this:
If we look at the next few lines of the Form's constructor, we see statements like:
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
This method retrieves the text field's model by calling
getModel(), which is implemented like this:
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:
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
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:
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
getStringProperty() method would be called on the
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
RequiredTextField's in the FormInput form, a conversion may occur based on the third parameter to the Component. For example, this component:
will convert the contents of the text field to and from an Integer value.
BoundCompoundPropertyModel has been removed in Wicket 6!
In the event that you need more flexibility or property expressions 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 expression and/or type conversion:
To see how this might be used, suppose that the
stringProperty value needed to be bound to the property expression
"person.stringProperty". In the
FormInput constructor, we'd say something like this:
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.
StringResourceModel-s 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:
A very simple example of a
StringResourceModel might be a
Label constructed like this:
where the resource bundle for the page contains an entry like this:
By adding a model, we can use the 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:
and have a resource like:
User has a
getName() method exposing its
Component class has convenience method which you may find useful:
If we do not want to use the property expressions/introspection, but instead want to to pull a fresh value from the model on each request in a more strongly typed manner, we could provide our own implementation of
Note that in the above example, we extended from
Model instead of implementing
IModel directly, and we did not bother writing
setObject() (as for the label it is not needed).
By implementing your own models, you can support any kind of model behaviour you can think of, though it is usually a more verbose way of doing things.
All model classes in Wicket have 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
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,
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:
Which state a detachable object is in is available via:
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).
The transient object is loaded in the
getObject() method, if necessary.
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.
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.
Now we have an example of a functioning detachable model that shrinks and then restores its state when replicated in a cluster.
AbstractReadOnlyModel is another simple implementation of
IModel. It has empty logic for detachment and disallows setting an model object, thus becoming read-only model.
StringResourceModel we talked about above is 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
The first example of property model is
AbstractPropertyModel. It provides implementation details for the
BoundCompoundPropertyModel classes we discussed above by implementing
setObject() using a property expression and conversion type retrieved from a subclass via abstract method
propertyExpression(). The simple
PropertyModel class just returns the property expression and type it was constructed with:
The conversion type is determined in the method
getObjectClass() provided by
Another type of property model,
CompoundPropertyModel, returns the name of the component as its property expression:
BoundCompoundPropertyModel stores a set of bindings internally and returns the property expression for a component based on this information:
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
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.
You already have seen some Wicket components in action. You also learned that you can influence what the components actually do by providing them with proper models. Now it is time to look at how to customize Wicket components.
Usually you do not want to start from scratch when you customize a component. Most components can be extended, and have methods that are explicitly meant for customization.
Some components do not even work without this, like
wicket.markup.html.link.Link, which has abstract method
wicket.markup.html.list.ListView that has abstract method
wicket.markup.html.form.DropDownChoice is a non-abstract component, but by extending it, we can alter its behaviour. For example, look at:
By overloading method
wantOnSelectionChangedNotifications(), and have it return
true (instead of
And if we want to customize the component even further, we could do:
and do something nice in the
onSelectionChanged() event handler.
By making use of models that use e.g. a shared property, we can do all kinds of neat things. Here is an example:
Note that we fed our dropdown with a property model that works on
this (e.g. a Page) using property 'currentVenue'. Now, with the second component we override method
isVisible, and use that same property 'currentVenue' for our evaluation.
If any venue is selected, we'll display the container and anything that is nested on it. If no venue is selected, we do not display anything. This is a very handy trick to switch parts of your page on and off, and as the dropdown will generate a roundtrip on each selection, our page will feel very dynamic.
TODO... write more
Panels and Borders
See also Panels and borders to see the difference between Panels and Borders.
Both Panels and Borders have their own markup file, but the markup is used in different ways:
The Border component inserts markup before and after its child components.
The Panel component inserts markup into the body of the tag.
See Panel for more information.
Doing it (almost) from scratch
Start with an example using the pojo from 4
Subclass wicket.markup.html.WebPage and in the constructor initialize and add the required Form implementation.
Subclass wicket.markup.html.form.Form and in the constructor add the necessary form elements.
These will be bound to the individual html form elements through the wicket:id attributes.
The html form itself must contain a wicket:id attribute that matches the Form added to the WebPage. Each form element must also have a corresponding wicket:id to each FormComponent added to the Form. In this example the form is bound to the "customerForm" Form instance and the text input is bound to the "name" TextField. There is also a single submit button that is not bound to any component. Wicket automatically provides the submission handling functionality through the Form base class implementation. Button components only need to be added to the Form if multiple buttons are required.
Updating the model
What happens when the form is submitted? Describe how the automatic updating of pojo's when property models are used. Describe how to use buttons and a button-less form.
NO LONGER VALID AS OF RC1, FORM parent method doesn't take a FeedbackPanel
Describe how validation works and how to implement custom validation. Also give a short explanation of how to display messages.
Adding RequiredValidator to a form element will allow only non-null and non-empty values to pass validation successfully.
For convenience the class RequiredTextField is provided. This TextField automatically includes a RequiredValidator.
To provide validation feedback a FeedbackPanel can be instantiated. This should be added to the page (for display) and associated with the form by passing it into the form constructor.
The FeedbackPanel can then be added to the html with a wicket:id span.
Finally, create a .properties file in the same directory as your html template and class file using the same naming conventions. In this case it would be called EditCustomerPage.properties. Use this file to define the error messages for each input in the form.
Describe how conversion works (when using property models) and how to implement custom conversion.
Give a list with a short description of the form components that are available in core.
TODO Describe the life cycle of Wicket apps, sessions and requests.
See Request processing overview. It's not exactly description of life cycles but is related to that.
The wicket way of storing objects in your session is by extending the wicket session. For example if you want to store some kind of user object in your session, you would create a getter and setter for it in
your custom wicket session:
Add the property to your custom Session object.
and override either of the following methods on your Application class:
retreive the Session by
Describe how to use custom request cycles.
TODO Describe the IRequestListener, and how the ILinkListener and IOnChangeListener interfaces were implemented. This chapter will give users a better, in-depth understanding of Wicket's workings.
See Request targets. It's not about listeners, but has small diagram of listeners classes hierarchy and sequence diagram of how RequestListenerInterface is used.
Localization and Skinning of Applications
Wicket provides a wide range of features that make it possible to easily create web applications for different locales and with different skins (styles). First, lets take a look at the basic localization and skinning features of Wicket. After that we will move on to how you can extend this functionality to take full control over localization.
Locale and Style
Wicket will use the locale of the incoming request along with a Style identifier to load resources. These resources include
- Markup files
- localization resource bundles (properties files)
- form input validators (also properties files)
Locale is set in the Wicket WebRequest object from the http request header locale in the User Agent field. Optionally, a user's locale can be set in their Session object by calling setLocale.
Style enables skinning of the application and is set by calling setStyle on the Wicket Session object. Session.get() returns the Session object associated with the current request.
Additionally, classes derived from Component provide a getVariation method which the developer can override on a per component basis to further discriminate resources to load.
Describe the loading of markup files by locale and style.
The ResourceStreamLoader determines what markup file to load for a component based on the variant, style, and locale values set for that component.
The Pub example in wicket-examples demonstrates how html markup is chosen based on locale. There is a default Home.html, Home_de_DE.html for users with German locale set, Home_zh_CN.html for Mandarin, etc.
These pages can be skinned by setting the Style attribute in the user's session. You could have three different styles for home: stone, wood, and brick. If the style you set does not have a corresponding markup file, then the localized markup is returned. If there is no markup for the locale, the default markup is returned. You can skin the Home page for a German user by providing Home_stone_de_DE.html, Home_wood_de_DE.html, and Home_brick_de_DE.html.
There is also a variation attribute that is additive to the style attribute. If no style attribute has been set, then variation takes the place of style in the resource name. If style is set, variation + "_" is prepended to the style name, which is then returned by getStyle().
If you have a panel inside Home called kitchen, you can override getVariation in the kitchen panel to return "granite" or "corriander". For a German locale, you could provide the following markups for the granite variation: Kitchen_granite_de_DE.html, Kitchen_granite_stone_de_DE.html, Kitchen_granite_wood_de_DE.html, Kitchen_granite_brick_de_DE.html. The possibilities are exponential.
The javadoc for the Wicket Session class describes the algorithm for resolving markup resources to load:
Wicket can also manage message localization with ResourceBundle properties files.
The Localizer is a utility class that encapsulates all of the localization related functionality in Wicket so it can be accessed by all areas of the framework in a consistent way. All classes derived from Component have a getLocalizer() method which retrieves the shared Localizer from the Application. The Localizer maintains a static cache of messages already retrieved by a key generated from the class, locale, style and messageKey for the message. Message bundles are looked up using classes derived from AbstractStringResourceLoader. The Localizer fetches a list of AbstractStringResourceLoader from the Application object. By default, the Application provides a ComponentStringResourceLoader and a ClassStringResourceLoader.
In the default setup, the ComponentStringResourceLoader is first asked to locate the message bundle. If it can't find one (using the algorithm described below), the application's ClassStringResourceLoader is consulted next; this attempts to find a single resource bundle that has the same name and location as the application
The algorithm employed by ComponentStringResourceLoader to resolve message requests is described in the javadoc for the ComponentStringResourceLoader:
The component based string resource loader attempts to find the resource from a bundle that corresponds to the supplied component object or one of its parent containers. Generally the component will be an instance of Page, but it may also be an instance of any reusable component that is packaged along with its own resource files. If the component is not an instance of Page then it must be a component that has already been added to a page.
The search order for resources is built around the containers that hold the component (if it is not a page). Consider a Page that contains a Panel that contains a Label. If we pass the Label as the component then resource loading will first look for the resource against the page, then against the panel and finally against the label.
An exception will be thrown if you are using Localizer on a component that has not already been added to a page. Unless you are sure your component has already been attached to the Page hierarchy, use StringResourceModel, which will defer message bundle lookup until after the component has been attached to a page (during the render process).
The naming convention for resource lookup is the similar to the convention to markup files. The resource loader is style and locale aware. For example, you have a page Home with a panel Kitchen that has a panel Sink, and Sink has labels LeftFawcett (key fawcett.left) and RightFawcett (key fawcett.right) that need to be localized. Your locale is German and your style is "standard". The ComponentStringResourceLoader will first look for fawcett.left and fawcett.right in Home_standard_de_DE.properties, then the same extension for Kitchen, and then Sink. If the key is not found, it will continue following the same steps detailed above for the markup files, but for each step trying it for Home, then Kitchen, then Sink
You can always get localizer inside a component by calling
getLocalizer(). The localizer provides various
getString methods, two of the most important ones are:
getString(key, component, model)
You can use first one if your property does not contain any strings which should be substituted. The second one is used in case if your property contains values which should be replaces, such as
In this case you can either provide an object which contains getters for
units as model or you can provide a
MapModel which maps
units to desired replacements. For example:
The Localizer contains powerful string substitution methods for manipulating message values. Examples are provided in the javadoc for StringResourceModel, such this:
Where the resource bundle contains the entry
For Localizing components, you mostly will want to rely on StringResourceModel. As mentioned before, you cannot call getLocalizer on a component before it has been attached to the framework (added to a hierarchy that has a Page). StringResourceModel uses Localizer inside it, but does not get rendered until after the component heirarchy has been established, so no exceptions. The StringResourceModel offers all the features of Localizer, plus the application of substitution parameters by java.text.MessageFormat. Again, there is an example demonstrating this in the StringResourceModel javadocs:
And where the resource bundle entry is:
FormValidators derived from AbstractValidator or StringValidator also exploit Wicket's localization capabilities. Inside, they delegate localization to the Localizer. There must be a resource bundle properties file in the Localizer search path that has a messageKey equivalent to form-name.component-name.validator-class. This allows the validators to be Style and locale aware. Substitution is enabled, but parameterized MessageFormat is not.
Describe the loading of other resources by locale and style.
String Resource Loaders
Describe how to write new string resource loaders.
Scalability and Session Management
Write about the following:
- How session attributes are managed
- How to control what end up in the session
- Future directions
Unit testing with Wicket is done by mocking the application. This means that you do not have a webserver running. This section will be about how to get started using unit testing with Wicket's testing framework.
Above example makes sure that your page can render. This in itself is a very powerful test & and should be the minimum of tests on all your pages (in this author's opinion).
Basic Test Case for some labels and elements ...
Note that in above test, should you have panel without a default constructor, then you'd have to do the following :
Similiar to above, if you have a page without a default constructor; you would do the same; this time creating an anonymous ITestPageSource implementation.
In above test, as you can make out, first a WicketTester class is instantiated, This would create a DummyWebApplication instance, which will have new SessionStore, Response etc. and a DummyHomePage in it. A call to startPage method on wicket Tester initiates the cycle. First Mock request and response are setup,then a new RequestCycle is created followed by regular request processing (for details see Lifecycle section of documentation).
A note on paths : As in above assertLabel( ) we have to precede the actual label name or component name with container,in this case a panel.