Professional Documents
Culture Documents
by Andrea Del Bene (an.delbene@gmail.com) proof reading by Paul Bors (paul@bors.ws) Revision date: 2013-05-30
License
This document is licensed under the Attribution- on!ommercial-"hareAli#e $.% &nported 'ou are free( to "hare ) to copy* distribute and transmit the wor# to +emi, ) to adapt the wor# &nder the following conditions( Attribution ) 'ou must attribute the wor# in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the wor#). oncommercial ) 'ou may not use this wor# for commercial purposes. "hare Ali#e ) -f you alter* transform* or build upon this wor#* you may distribute the resulting wor# only under the same or similar license to this one. The full license is a.ailable at http(//creati.ecommons.org/licenses/by-nc-sa/$.%/.
We should invent a miracle...yes, we should get to the point where our egoism magically coincides with the happiness of others. -0iorgio 0aber* singer* poet and actor.
Table of Contents
Table of !ontents
Preface..................................................................................................................................... 1 How to use the example code................................................................................................2 Graphic conventions.............................................................................................................. 3 1 Wh should ! learn Wicket".................................................................................................#
1.1 2e all li#e spaghetti (-) ..................................................................................................................................... 3 1.4 !omponent oriented framewor#s( an o.er.iew.................................................................................................3 1.$ Benefits of component oriented framewor#s for web de.elopment...................................................................5 1.3 2ic#et .s the other component oriented framewor#s.......................................................................................5
# )eeping control over H*+,...............................................................................................23.1 9iding or disabling a component..................................................................................................................... 4A 3.4 <odifing tag attributes..................................................................................................................................... 4A 3.$ 0enerating tag attribute BidB ............................................................................................................................ 46 3.3 !reating in-line panels with 2eb<ar#up!ontainer.........................................................................................46 3.5 2or#ing with mar#up fragments...................................................................................................................... 47 3.A Adding header contents to the final page........................................................................................................48 3.6 &sing stub mar#up in our pages/panels..........................................................................................................$% 3.7 9ow to render component body only...............................................................................................................$% 3.8 9iding decorating elements with the >wic#et(enclosure? tag..........................................................................$1 3.1% "urrounding e,isting mar#up with Border.....................................................................................................$4 3.11 "ummary....................................................................................................................................................... $$
Table of Contents
A.3 "ummary......................................................................................................................................................... 35
' 0nder the hood of the re1uest processing.......................................................................#6.1 !lass Application and reEuest processing......................................................................................................3A 6.4 +eEuest and +esponse classes..................................................................................................................... 3A 6.$ The FdirectorG of reEuest processing( +eEuest!ycle.......................................................................................3A 6.$.1 +eEuest!ycle and reEuest processing...................................................................................................36 6.$.4 0enerating &+: with the url=or and map&rl=or methods......................................................................37 6.$.$ <ethod set+esponsePage..................................................................................................................... 37 6.$.3 +eEuest!ycleBs hoo# methods and listeners..........................................................................................37 6.3 "ession !lass.................................................................................................................................................. 38 6.3.1 "ession and listeners.............................................................................................................................. 38 6.3.4 9andling session attributes..................................................................................................................... 5% 6.3.$ Accessing to the 9TTP session..............................................................................................................5% 6.3.3 Temporary and permanent sessions......................................................................................................51 6.3.5 Discarding session data.......................................................................................................................... 54 6.5 "toring arbitrary obHects with metadata...........................................................................................................54 6.A "ummary......................................................................................................................................................... 5$
ii
Table of Contents
1%.7 !reating comple, form components with =orm!omponentPanel.................................................................86 1%.8 "tateless form............................................................................................................................................. 1%% 1%.1% 2or#ing with radio buttons and chec#bo,es.............................................................................................1%4 1%.1%.1 2or#ing with grouped chec#bo,es...................................................................................................1%3 1%.1%.4 9ow to implement a F"elect allG chec#bo,........................................................................................1%5 1%.1%.$ 2or#ing with grouped radio buttons.................................................................................................1%A 1%.11 "electing multiple .alues with :ist<ultiple!hoices and Palette ...............................................................1%A 1%.11.1 !omponent Palette........................................................................................................................... 1%6 1%.14 "ummary................................................................................................................................................... 1%8
iii
Table of Contents
15.5 &sing K<L with 2ic#et................................................................................................................................ 15% 15.A 0enerating 9T<: mar#up from code..........................................................................................................154 15.A.1 A.oiding mar#up caching....................................................................................................................15$ 15.6 "ummary..................................................................................................................................................... 153
i.
Table of Contents
Alpha=etical !ndex..............................................................................................................21(
Preface
Preface
2ic#et has been around since 4%%3 and it has been an Apache proHect since 4%%6. During these years it has pro.ed to be a solid and .aluable solution for building enterprise web applications. 2ic#et core de.elopers ha.e done a wonderful Hob with this framewor# and they continue to impro.e it release after release. 9owe.er 2ic#et ne.er pro.ided a freely a.ailable documentation and e.en if you can find on -nternet many li.e e,amples and many technical articles on it (most of them at http(//www.wic#et-library.com/ and at http(//wic#etinaction.com/)* the lac# of an organiCed and freely a.ailable documentation has always been a sore point for this framewor#. ThatBs Euite an issue because many other popular framewor#s (li#e "pring* 9ibernate or "truts) offer a .ast and .ery good documentation which substantially contributed to their success. This document is not intended to be a complete reference for 2ic#et but it simply aims to be a straightforward introduction to the framewor# that should significantly reduce its learning cur.e. 2hat you will find here reflects my e,perience with 2ic#et and itBs strictly focused on the framewor#. The .arious 2ic#et-related topics are gradually introduced using pragmatic e,amples of code that you can find at https(//github.com/bitstorm/2ic#et-tutorial-e,amples. 9owe.er remember that 2ic#et is a .ast and powerful tool* so you should feel confident with the topics e,posed in this document before starting to code your real applications; =or those who need further documentation on 2ic#et* there are many good boo#s a.ailable for this framewor#. 'ou can find an e,hausti.e list of these boo#s at http(//wic#et.apache.org/learn/boo#s/ 9ope youBll find this guide helpful. 9a.e fun with 2ic#et; Andrea Del Bene* an.delbene@gmail.com PS: this guide is based on Wicket 6. However if you are using an older version you should find this guide useful as well, but it's likely that the code and the snippets won't work with your version. PPS: although I've did my best working on this tutorial, this document is a work in progress and may contain errors and or omissions. !hat's why any feedback of any kind is "#$%%& appreciated'
-f you arenBt used to 0it* you can simply download the whole source as a Cip archi.e(
The repository contains a multi-module <a.en proHect. I.ery subproHect is contained in the relati.e folder of the repository(
2hen the e,ample code is used in the document* you will find the name of the subproHect it belongs to. -f you donBt ha.e any e,perience with <a.en* you can read Appendi, A where you can learn the basic commands needed to wor# with the e,ample proHects and to import them into your fa.ourite -DI ( etBeans* -DIA or Iclipse).
Graphic conventions
Graphic conventions
To ma#e reading easier* some graphic con.entions ha.e been adopted(
!ode reference inside te,t are written with a different font ( FreeMono)( ...the .ariable message is....
!ode bloc#s are formatted and coloured following the default Iclipse style(
/** * This is about <code>ClassName</code>. * !link com."ourCompan".a#ackage.$nter%ace& * !author author * !deprecated use <code>'therClass</code> */ public class ClassName<(> implements $nter%aceName<)tring> enum Color
/* This comment ma" span multiple lines. */ static 'b2ect static3ield1 // This comment ma" span onl" this line pri4ate ( %ield1 // T5)6: re%actor !)uppressWarnings74alue89all9: public int %oo7$nteger parameter: abstract;ethod7:1 int local8 <=*hashCode7:1
-mportant informations and warnings are written inside bloc#s li#e these( >ote bla...bla...bla....
Warning bla...bla...bla....
llustration 1$1% General schema of page re&uest handling for a component oriented framework
2 "b#ect "riented
2ith this #ind of framewor# our web pages and their 9T<: components (forms* input controls* lin#s* etc...)* are pure class instances. "ince pages are class instances they li.e inside the KD< heap and we can handle them as we do with any other Ka.a class. This approach is .ery similar to what 0&- framewor#s (li#e "wing or "2T) do with des#top windows and their components. 2ic#et and the other component oriented framewor#s bring to web de.elopment the same #ind of abstraction that 0&- framewor#s offer when we build a des#top application. <ost of those #ind of framewor#s hide the details of the 9TTP protocol and naturally sol.e the problem of its stateless nature.
We= pages are o=<ects( web pages are not Hust te,t files sent bac# to the client. They are obHect instances and we can harness @@P to design web pages and their components. 2ith 2ic#et we can also apply inheritance to 9T<: mar#up in order to build a consistent graphic layout for our applications (we will see markup inheritance in chapter $). We donAt have to worr a=out applicationAs state ( pages and components can be considered stateful entities. They are Ka.a obHects and they can #eep a state inside them and reference other obHects. 2e can stop worrying about #eeping trac# of user data stored inside the HttpSession and we can start managing them in a natural and transparent way. *esting we= applications is much easier ( since pages and components are pure obHects* you can use K&nit to test their beha.ior and to ensure that they render as e,pected. 2ic#et has a set of utility classes for unit testing that simulate user interaction with web pages* hence we can write acceptance tests using Hust K&nit without any other test framewor# (unit testing is co.ered in chapter 18).
Wicket is 1//B open source: 2ic#et is a top Apache proHect and it doesnBt depend on any pri.ate company. 'ou donBt ha.e to worry about future licensing changes* 2ic#et will always be released under Apache license 4.% and freely a.ailable. Wicket is a communit driven pro<ect: The 2ic#et team supports and promotes the dialogue with the framewor#Bs users through two mailing lists (one for users and another one for framewor# de.elopers)$ and an Apache K-+A3 (the issue trac#ing system). <oreo.er* as any other Apache proHect* 2ic#et is de.eloped paying great attention to user feedbac#s and to suggested features.
3 4
'ee http: wicket.apache.org help email.html https: issues.apache.org -ira browse .I(/#!
Wicket is <ust a=out 7ava and good old H*+,: almost all web framewor#s force users to adopt special tags or to use ser.er side code inside 9T<: mar#up. This is clearly in contrast with the concept of separation between presentation and business logic and it leads to a more confusing code in our pages. -n 2ic#et we donBt ha.e to ta#e care of generating 9T<: inside the page itself* and we wonBt need to use any tag other than standard 9T<: tags. All we ha.e to do is to attach our components (Ka.a instances) to the 9T<: tags using a simple tag attribute called wicket:id (we will shortly see how to use it). With Wicket we can easil use 7ava;eans and PC7C ( in our we= tire: one of the most annoying and error-prone tas# in web de.elopment is collecting user input through a form and #eeping form fields updated with pre.iously inserted .alues. This usually reEuires a huge amount of code to e,tract input from reEuest parameters (which are strings)* parse them to Ka.a types and store them into some #ind of .ariable. And this is Hust half of the wor# we ha.e to do as we must implement the in.erse path (load data from Ka.a to the web form). <oreo.er* most of the times our forms will use a Ka.aBean or a P@K@ as bac#ing obHect* meaning that we must manually map form fields with the corresponding obHect fields and .ice .ersa. 2ic#et comes with an intuiti.e and fle,ible mechanism that does this mapping for us without any configuration o.erhead (using a convention over configuration approach) and in a transparent way. !hapter 8 will introduce the concept of 2ic#et model and we will learn how to harness this entity with forms. >o complex 9+, needed: 2ic#et was designed to minimiCe the amount of configuration files needed to run our applications. o L<: file is reEuired e,cept for the standard deployment descriptor web.0ml.
-n both framewor#s we find a base class for 0&- components called Component. 2ic#et pages can be composed (and usually are) by many components* Hust li#e A2T windows are composed by "wing/A2T components. Both framewor#s promote the reuse of presentation code and 0&- elements building custom components. I.en if 2ic#et already comes with a rich set of ready-to-use components* building custom components is a common practice when wor#ing with this framewor#. 2eBll learn more about custom components in the ne,t chapters.
+oduleAsname
wic#et-core wic#et-reEuest wic#et-util
5escription
5ependencies
!ontains the main classes of the framewor#* li#e - wic#et-reEuest class Component and Application. - wic#et-util This module contains the classes in.ol.ed into web - wic#et-util reEuest processing. !ontains general-purpose utility functional areas such as -/@* manipulation* security* etc... classes for lang* string one.
wic#et-datetime wic#et-de.utils
!ontains special purpose components designed to -wic#et-core wor# with date and time. !ontains utility classes and components to help -wic#et-core de.elopers with tas#s such as debugging* class -wic#et-e,tensions inspection and so on. !ontains a .ast set of built-in components to build -wic#et-core a rich &- for our web application (AHa, support is part of this module). Pro.ides support for role-based authoriCation. -wic#et-core
wic#et-e,tensions
wic#et-auth-roles wic#et-ioc
This module pro.ides common classes to support -wic#et-core -n.ersion @f !ontrol. -tBs used by both "pring and 0uice integration module. This module pro.ides integration with the -wic#et-core dependency inHection framewor# de.eloped by -wic#et-ioc 0oogle. This module pro.ides integration with "pring -wic#et-core framewor#. -wic#et-ioc This module pro.ides panels and utility class to -wic#et-core integrate 2ic#et with Delocity template engine. This module pro.ides panels and utility class to -wic#et-core integrate 2ic#et with 3ava 4anagement #0tensions. Pro.ides integration with Ka.a agent libraries and -wic#et-core instrumentation tools.
wic#et-guice
wic#et-obHectsiCeof-agent
Please note that the core module depends on the utility and re5uest modules* hence it cannot be used without them.
2.2.1
A 2ic#et application is a standard Ka.a II web application* hence it is deployed through a web.0ml file placed inside folder 2IB-- =A(
llustration *$1% The standard directory structure of a Wicket application The content of web.,ml declares a ser.let filter (class org.apache.wicket.Protocol.http. WicketFilter) which dispatches web reEuests to our 2ic#et application(
<?xml 4ersion89@.A9 encoding890T3-B9?> <Ceb-app> <displa"-name>Wicket Test</displa"-name> <%ilter> <%ilter-name>Test5pplication</%ilter-name> <%ilter-class>org.apache.Cicket.protocol.http.Wicket3ilter</%ilter-class> <init-param> <param-name>applicationClassName</param-name> <param-4alue>org.CicketTutorial.Wicket5pplication</param-4alue> </init-param> </%ilter> <%ilter-mapping> <%ilter-name>Test5pplication</%ilter-name> <url-pattern>/*</url-pattern> </%ilter-mapping> </Ceb-app>
mapping>
"ince this is a standard ser.let filter we must map it to a specific set of &+:s through the tag). -n the ,ml abo.e we ha.e mapped e.ery &+: to our 2ic#et filter. >ote
<%ilter-
2ic#et can be started in two modes named respecti.ely 5DED,CP+D>* and 5DP,CF+D>*. The first mode acti.ates some e,tra features which help application de.elopment* li#e resources monitoring and reloading* full stac# trace rendering of e,ceptions* an AKAL debugger window* etc... The DIP:@'<I T mode turns off all these features optimiCing performances and resource consumption. -n our e,ample proHects we will use the default mode which is DIDI:@P<I T. Appnedi, A contains the chapter F"witching 2ic#et to DIP:@'<I T modeF where we can find further details about these two modes as well as the possible ways we ha.e to set the desired one. -n any case* 5C >C* deploy your applications in a production en.ironment without switching to DIP:@'<I T mode;
2.2.2
-f we loo# bac# at web.0ml we can see that we ha.e pro.ided the 2ic#et filter with a parameter called applicationClassName. This .alue must be the fully Eualified class name of a subclass of org.
Wicket free user guide
apache.wicket.Application. This subclass represents our web application built upon 2ic#et and itBs responsible for configuring it when the ser.er is starting up. <ost of the times our custom application class wonBt inherit directly from class Application* but rather from class org.apache.wicket .protocol.http.WebApplication which pro.ides a closer integration with ser.let infrastructure. !lass Application comes with a set of configuration methods that we can o.erride to customiCe our applicationBs settings. @ne of these methods is getHomePage( that must be o.erridden as it is declared abstract(
public abstract Class<? extends #age> getDome#age7:
As you may guess from its name* this method specifies which page to use as homepage for our application. Another important method is init( (
protected 4oid init7:
This method is called when our application is loaded by the web ser.er (Tomcat* Ketty* etc...) and is the ideal place to put our configuration code. The Application class e,poses its settings grouping them into interfaces (you can find them in pac#age org.apache.wicket.settings). 2e can access these interfaces through getter methods that will be gradually introduced in the ne,t chapters when we will co.er the related settings. The current applicationBs instance can be retrie.ed at any time calling static method Application.get( in our code. 2e will gi.e more details about this method in paragraph 6.$. The content of the application class from proHect Hello.orld#0ample is the following(
public class Wicket5pplication extends Web5pplication !'4erride public Class<? extends Web#age> getDome#age7: return Dome#age.class1 & !'4erride public 4oid init7: super.init7:1 // add "our con%iguration here & &
"ince this is a .ery basic e,ample of a 2ic#et application* we donBt need to specify anything inside the init method. The home page of the application is the HomePage class. -n the ne,t paragraph we will see how this page is implemented and which con.entions we ha.e to follow to create a page in 2ic#et. >ote Declaring a WicketFilter inside web.0ml descriptor is not the only way we ha.e to #ic#start our application. -f we prefer to use a ser.let instead of a filter* we can use class org.apache.
1%
llustration *$*%Page class and its related HT/0 file -f you donBt li#e to put class and html side by side (letBs say you want all your 9T<: files in a separated folder) you can use 2ic#et settings to specify where 9T<: files can be found. 2e will co.er this topic later in paragraph 1$.8. The Ka.a code for the HomePage class is the following(
package org.CicketTutorial1 import org.apache.Cicket.reEuest.mapper.parameter.#age#arameters1 import org.apache.Cicket.markup.html.basic./abel1 import org.apache.Cicket.markup.html.Web#age1 public class Dome#age extends Web#age public Dome#age7: add7neC /abel79hello;essage9, 9Dello WicketWorldF9::1 & &
Apart from subclassing WebPage* HomePage defines a constructor that adds a Label component to itself. <ethod add(Component component is inherited from ancestor class org.apache.wicket. Mark"pContainer and is used to add children components to a web page. 2eBll see more about Mark"pContainer later in chapter $.4.4. !lass org.apache.wicket.mark"p.html.basic.Label is the simplest component shipped with 2ic#et. -t Hust inserts a string (the second argument of its constructor) inside the corresponding 9T<: tag. Kust li#e any other 2ic#et component* Label needs a te,tual id (#helloMessage# in our e,ample) to be instantiated. At runtime 2ic#et will use this .alue to find the 9T<: tag we want to bind to the component. This tag must ha.e a special attribute called Cicket:id and its .alue must be identical to the component id (comparison is case-sensiti.e;). 9ere is the 9T<: mar#up for HomePage (file 9omePage.html)(
Wicket free user guide
11
<F+'CTG#( html> <html> <head> <meta charset89ut%-B9 /> <title>5pache Wicket DelloWorld</title> </head> <bod"> <di4 Cicket:id89hello;essage9> H/abelIs message goes hereJ </di4> </bod"> </html>
2e can see that the Cicket:id attribute is set according to the .alue of the component id. -f we run this e,ample we will see the te,t Hello WicketWorld$ -nside a <di4> tag. >ote Label replaces the original content of its tag (in our e,ample %Label#s message goes here&) with the string passed as .alue ( Hello WicketWorld$ in our e,ample). Warning -f we specify a Cicket:id attribute for a tag without adding the corresponding component in our Ka.a code* 2ic#et will throw a ComponentNotFo"nd '(ception. @n the contrary if we add a component in our Ka.a code without specifying a corresponding Cicket:id attribute in our mar#up* 2ic#et will throw a Wicket)"ntime'(ception.
14
By default after onClick has been e,ecuted* 2ic#et will send bac# to the current page to the client web browser. -f we want to na.igate to another page we must use method set)esponsePage of class Component(
public class Dome#age extends Web#age public Dome#age7: add7neC /ink79id9: !'4erride public 4oid onClick7: //Ce redirect broCser to another page. set*esponse#age75nother#age.class:1 & &:1 & &
-n the e,ample abo.e we used a .ersion of set)esponsePage which ta#es as input the class of the target page. -n this way a new instance of AnotherPage will be create each time we clic# on the lin#. The other .ersion of set)esponsePage ta#es in input a page instance instead of a page class(
//... !'4erride public 4oid onClick7: //Ce redirect broCser to another page. 5nother#age another#age 8 neC 5nother#age7:1 set*esponse#age7another#age:1 & //...
The difference between using the first .ersion of set)esponsePage rather than the second one will be illustrated in chapter A* when we will introduce the topic of stateful and stateless pages. =or now* we can consider them as eEui.alent. >ote 2ic#et comes with a rich set of lin# components suited for e.ery need (lin#s to static &+:* AHa,-enhanced lin#s* lin#s to a file to download* lin#s to e,ternal pages and so on). 2e will see them in chapter 7.
2.( 8ummar
-n this chapter we ha.e seen the basic elements that compose a 2ic#et application. 2e ha.e started preparing the configuration artifacts needed for our applications. As promised in paragraph 1.3* we needed to put in place Hust a minimal amount of L<: with an application class and a home page.
1$
Then we ha.e continued our Ffirst contactG with 2ic#et learning how to build a simple page with a label component as child. This e,ample page has shown us how 2ic#et maps components to 9T<: tags and how it uses both of them to generate the final 9T<: mar#up. -n the last paragraph we had a first taste of 2ic#et lin#s and we ha.e seen how they can be considered as a Fclic#G e.ent listener and how they can be used to na.igate from a page to another.
13
llustration 1$1% The new look of )ava.oc 2 2ith the adoption of ser.er side technologies li#e K"P* A"P or P9P the tag <%rame> has been replaced by a template-based approach where we di.ide our page layout into some common areas that will be present in each page of our web application. Then* we manually insert these areas in e.ery page including the appropriate mar#up fragments. -n this chapter we will see how to use 2ic#et to build a site layout. The sample layout we will use is a typical page layout consisting of the following areas( a header which could contain site title* some logos* a na.igation bar* etc... a left menu with a bunch of lin#s to different areas/functionalities of the site. a footer with generic informations li#e web masterBs email* the company address* etc...
Wicket free user guide
15
a content area which usually contains the functional part of the page.
llustration 1$*% 7n abstract view of layout areas @nce we ha.e chosen a page layout* our web designer can start building up the site theme. The result is a beautiful moc# of our future web pages. @.er this moc# we can map the original layout areas 6(
1A
ow in order to ha.e a consistent layout across all the site* we must ensure that each page will include the layout areas seen abo.e. 2ith an old template-based approach we must manually put them inside e.ery page. -f we were using K"P we would probably end up using incl"de directi.e to add layout areas in our pages. 2e would ha.e one incl"de for each of the areas (e,cept for the content)(
llustration 1$3% 0ayout areas are assembled with include directive >ote =or the sa#e of simplicity we can consider each included area as a static 9T<: fragment. ow letBs see how we can handle the layout of our web application using 2ic#et.
3.2.1
+arkup inheritance
As we ha.e seen in the pre.ious chapter* 2ic#et pages are pure Ka.a classes* so we can easily write a page which is a subclass of another parent page. But in 2ic#et inheritance is not limited to the classic obHect-oriented code inheritance. 2hen a class subclasses a WebPage it also inherits the 9T<: file of to the parent class. This type of inheritance is called markup inheritance. To better illustrate this concept letBs consider the following e,ample where we ha.e a page class called *enericSitePage with the corresponding 9T<: file *enericSitePage.html. ow letBs create a specific page called +rderCheck+"tPage where users can chec# out their orders on our our web site.
16
This class e,tends *enericSitePage but we donBt pro.ide it with any corresponding 9T<: file. -n this scenario +rderCheck+"tPage will use *enericSitePage.html as mar#up file(
llustration 1$9% Page "rderCheck"utPage hasn:t a corresponding HT/0 file$ t will use the one from parent page 4General'itePage$html5$ <ar#up inheritance comes in handy for page layout management as it helps us a.oid the burden of chec#ing that each page conforms to the site layout. 9owe.er to fully ta#e ad.antage of mar#up inheritance we must first learn how to use another important component of the framewor# that supports this feature( the panel. Warning -f no mar#up is found (nor directly assigned to the class* neither inherited from an ancestor) a Mark"pNotFo"nd'(ception is thrown.
3.2.2
Panel class
!lass org.apache.wicket.mark"p.html.panel.Panel is a special component which lets us reuse 0&- code and 9T<: mar#up across different pages and different web applications. -t shares a common ancestor class with WebPage class* which is org.apache.wicket.Mark"pContainer(
17
"ubclasses of Mark"pContainer can contain children components that can be added with method add(Componet... (seen in chapter 4.$). Mark"pContainer implements a full set of methods to manage children components. The basic operations we can do on them are( add one or more children components (with method add). remo.e a specific child component (with methods remo!e). retrie.e a specific child component with method get(String . The string parameter is the id of the component or its relati.e path if the component is nested inside other Mark"pContainers. This path is a colon-separated string containing also the ids of the intermediate containers tra.ersed to get to the child component. To illustrate an e,ample of component path* letBs consider the code of the following page(
;"#anel m"#anel 8 neC ;"#anel 79innerContainer9:1 add7m"#anel:1
!omponent M,Panel is a custom panel containing only a label ha.ing 9name9 as id. &nder those conditions we could retrie.e this label from the container page using the following path e,pression(
/abel name 8 7/abel:get79innerContainer:name9:1
replace a specific child component with a new component having the same id (with method replace). iterate thought children components with the iterator returned by method iterator or using .isitor pattern7 with methods !isitChildren.
Both Panel and WebPage ha.e their own associated mar#up file which is used to render the corresponding component. -f such file is not pro.ided* 2ic#et will apply markup inheritance loo#ing for a mar#up file through their ancestor classes. 2hen a panel is attached to a container* the content of its mar#up file is inserted into its related tag. 2hile panels and pages ha.e much in common* there are some notable differences between these two components that we should #eep in mind. The main difference between them is that pages can be rendered as standalone entities while panels must be placed inside a page to be rendered. Another important difference is the content of their mar#up file( for both WebPage and Panel this is a standard 9T<: file* but Panel uses a special tag to indicate which part of the whole file will be considered as mar#up source. This tag is <Cicket:panel>. A mar#up file for a panel will typically loo# li#e this(
<html> <head> <meta http-eEui489Content-T"pe9 content89text/html1 charset80T3-B9> ... </head> <bod"> <Cicket:panel> <F-- Gour markup goes here --> </Cicket:panel> </bod">
<Cicket:panel>
18
tag can be used by both web de.elopers and web designers to place some moc# 9T<: to show how the final panel should loo# li#e.
3.3.1
=irst* letBs build a custom panel for each layout area (e,cept for BcontentB area). =or e,ample gi.en the header area
we can build a panel called HeaderPanel with a related mar#up file called HeaderPanel.html containing the 9T<: for this area(
<html> <head> <meta http-eEui489Content-T"pe9 content89text/html1 charset80T3-B9> ... </head> <bod"> <Cicket:panel> <table Cidth89@AAK9 st"le89border: Apx none19> <tbod"> <tr> <td> <img alt89Lug<Tenda9 src89Cicket/a"outM%iles/logoM2ug<tenda.gi%9> </td> <td> <h@>-estione 5nagra%ica</h@> </td> </tr> </tbod"> </table> </Cicket:panel> </bod"> <html>
The class for this panel simply e,tends base class Panel(
package helloWorld.la"outTenda1 import org.apache.Cicket.markup.html.panel.#anel1 public class Deader#anel extends #anel public Deader#anel7)tring id: super7id:1 & &
=or each layout area we will build a panel li#e the one abo.e that holds the appropriate 9T<: mar#up. -n the end we will ha.e the following set of panels( HeaderPanel
4%
HooterPanel +enuPanel
!ontent area will change from page to page* so we donBt need a reusable panel for it.
3.3.2
*emplate page
ow we can build a generic template page using our brand new panels. -ts mar#up is Euite straightforward (
<html> <head> <meta http-eEui489Content-T"pe9 content89text/html1 charset80T3-B9> ... <F--$nclude C))--> ... </head> <bod"> <di4 id89header9 Cicket:id89header#anel9>header</di4> <di4 id89bod"9> <di4 id89menu9 Cicket:id89menu#anel9>menu</di4> <di4 id89content9 Cicket:id89content9>content</di4> </di4> <di4 id89%ooter9 Cicket:id89%ooter#anel9>%ooter</di4> </bod"> </html>
<di4>
The 9T<: code for this page implements the generic left-menu layout of our site. 'ou can note the 3 tags used as containers for the corresponding areas. The page class contains the code to physically assemble the page and panels(
package helloWorld.la"outTenda1 import org.apache.Cicket.markup.html.Web#age1 public class LugTemplate extends Web#age public static %inal )tring C'NT(NTM$+ 8 9contentComponent91 pri4ate Component header#anel1 pri4ate Component menu#anel1 pri4ate Component %ooter#anel1 public LugTemplate7: add7header#anel 8 neC Deader#anel79header#anel9::1 add7menu#anel 8 neC ;enu#anel79menu#anel9::1 add7%ooter#anel 8 neC 3ooter#anel79%ooter#anel9::1 add7neC /abel7C'NT(NTM$+, 9#ut "our content here9::1 & //getters %or la"out areas //... &
Done; @ur template page is ready to be used. ow all the pages of our site will be subclasses of this parent page and they will inherit the layout and the 9T<: mar#up. They will only substitute the Label inserted as content area with their custom content.
3.3.3
Hinal example
As final e,ample we will build the login page for our site. 2e will call it SimpleLoginPage. =irst* we
Wicket free user guide
41
need a panel containing the login form. This will be the content area of our page. 2e will call it LoginFormPanel and the mar#up is the following(
<html> <head> ... </head> <bod"> <Cicket:panel> <di4 st"le89margin: auto1 Cidth: <AK19> <%orm id89login3orm9 method89get9> <%ieldset id89login9 class89center9> <legend >/ogin</legend> <span >0sername: </span><input t"pe89text9 id89usernameN/><br/> <span >#assCord: </span><input t"pe89passCord9 id89passCord9 /> <p> <input t"pe89submit9 name89login9 4alue89login9/> </p> </%ieldset> </%orm> </di4> </Cicket:panel> </bod"> </html>
The class for this panel Hust e,tends Panel class so we wonBt see the relati.e code. The form of this panel is for illustrati.e purpose only. 2e will see how to wor# with 2ic#et forms in chapters 8 and 1%. "ince this is a login page we donBt want it to display the left menu area. ThatBs not a big deal as Component class e,poses a method called set-isible which sets whether the component and its children should be displayed. The resulting Ka.a code for the login page is the following(
package helloWorld.la"outTenda1 import helloWorld./ogin#anel1 import org.apache.Cicket.e4ent..roadcast1 import org.apache.Cicket.e4ent.$(4ent)ink1 public class )imple/ogin#age extends LugTemplate public )imple/ogin#age7: super7:1 replace7neC /ogin#anel7C'NT(NTM$+::1 get;enu#anel7:.setOisible7%alse:1 & &
@b.iously this page doesnBt come with a related mar#up file. 'ou can see the final page in the following picture(
44
The mar#up of a child page/panel must be placed inside the tag <Cicket:extend>. @nly the mar#up inside <Cicket:extend> will be included in final mar#up. 9ere is an e,ample of child page mar#up(
<html> <head> <meta http-eEui489Content-T"pe9 content89text/html1 charset80T3-B9> ... </head> <bod"> <Cicket:extend> This is child bod"F </Cicket:extend> </bod"> </html>
!onsidering the two pages seen abo.e* the final mar#up generated for child page will be the following(
<html> <head> <meta http-eEui489Content-T"pe9 content89text/html1 charset80T3-B9> ... </head> <bod">
4$
This is parent bod"F <Cicket:child> <Cicket:extend> This is child bod"F </Cicket:extend> </Cicket:child> </bod"> </html>
3.#.1
Applying <Cicket:child> tag to our layout e,ample* we obtain the following mar#up for the main template page(
<html> <head> <meta http-eEui489Content-T"pe9 content89text/html1 charset80T3-B9> ... </head> <bod"> <di4 id89header9 Cicket:id89header#anel9>header</di4> <di4 id89bod"9> <di4 id89menu9 Cicket:id89menu#anel9>menu</di4> <Cicket:child/> </di4> <di4 id89%ooter9 Cicket:id89%ooter#anel9>%ooter</di4> </bod"> </html>
2e ha.e replaced the <di4> tag of the content area with the tag <Cicket:child>. 0oing forward with our e,ample we can build a login page creating class SimpleLoginPage which e,tends the ."g/emplate page* but with a related mar#up file li#e this(
<html> <head> ... </head> <bod"> <Cicket:extend> <di4 st"le89margin: auto1 Cidth: <AK19> <%orm id89login3orm9 method89get9> <%ieldset id89login9 class89center9> <legend >/ogin</legend> <span >0sername: </span><input t"pe89text9 id89usernameN/><br/> <span >#assCord: </span><input t"pe89passCord9 id89passCord9 /> <p> <input t"pe89submit9 name89login9 4alue89login9/> </p> </%ieldset> </%orm> </di4> </Cicket:extend> </bod"> </html>
As we can see this approach doesnBt reEuire to create custom panels to use as content area and it can
43
3.( 8ummar
2ic#et applies inheritance also to 9T<: mar#up ma#ing layout management much easier and less error-prone. Defining a master template page to use as base class for the other pages is a great way to build a consistent layout and use it across all the pages on the web site. During the chapter we ha.e also introduced the Panel component* a .ery important 2ic#et class that is primarily designed to let us di.ide our pages in smaller and reusable &- components.
45
"uppose we want to add some style to label content ma#ing it red and bolded. 2e can add to the label an Attrib"teModi0ier which creates the tag attribute st"le with .alue 9color:red1%ontCeight:bold9(
label.add7neC 5ttribute;odi%ier79st"le9, 9color:red1%ont-Ceight:bold9::1
-f attribute st"le already e,ists in the original mar#up* it will be replaced with the .alue specified by Attrib"teModi0ier. -f we donBt want to o.erwrite the e,isting .alue of an attribute we can use
9 The markup used to render disabled links can be customi<ed as described at the end of paragraph =$1
4A
subclass Attrib"teAppender which will append its .alue to the e,isting one(
label.add7neC 5ttribute5ppender79st"le9, 9color:red1%ont-Ceight:bold9::1
2e can also create attribute modifiers using factory methods pro.ided by class Attrib"teModi0ier and itBs also possible to prepend a gi.en .alue to an e,isting attribute(
// replaces existing 4alue Cith the gi4en one label.add75ttribute;odi%ier.replace79st"le9, 9color:red1%ont-Ceight:bold9::1
&nder those conditions we can consider using a WebMark"pContainer component rather than implementing a new panel. The code needed to handle the information bo, inside the page could be the following(
46
//#age initialiPation code ... Web;arkupContainer in%ormation.ox 8 neC Web;arkupContainer 79in%ormation.ox9:1 in%ormation.ox.add7neC /abel79messagesNumber9, messagesNumber::1 add7in%ormation.ox:1 ... //$% there are no neC messages, hide in%ormation.ox in%ormation.ox.setOisible7%alse:1
As you can see in the snippet abo.e we can handle our information bo, from Ka.a code as we do with any other 2ic#et component.
Page code(
3ragment %ragment 8 neC add7%ragment:1 3ragment 79content5rea9, 9%ragment$d9, this:1
=ragments can be .ery helpful with comple, pages or components. =or e,ample letBs say that we ha.e a page where users can register to our forum. This page should first display a form where user must insert his/her personal data (name* username* password* email and so on)* then* once the user has submitted1% the form* the page should display a message li#e F'our registration is complete; Please chec# your mail to acti.ate your user profile.G. -nstead of displaying this message with a new component or in a new page* we can define two
10 (orm component will be introduced in chapter >
47
fragments( one for the initial form and one to display the confirmation message. The second fragment will replace the first one after the form has been submitted( Page mar#up(
<html> ... <bod"> ... <di4 Cicket:id89content5rea9></di4> <Cicket:%ragment Cicket:id89%orm3rag9> <F-- 3orm markup goes here --> </Cicket:%ragment> <Cicket:%ragment Cicket:id89message3rag9> <F-- ;essage markup goes here --> </Cicket:%ragment> </bod"> </html>
Page code(
3ragment %ragment 8 neC add7%ragment:1 //... //%orm has been submitted 3ragment %ragment 8 neC 3ragment 79content5rea9, 9message3rag9, this:1 replace7%ragment:1 3ragment 79content5rea9, 9%orm3rag9, this:1
48
2ic#et will ta#e care of placing the content of <Cicket:head> inside the <head> tag of the final page. >ote The <Cicket:head> tag can also be used with children pages/panels which e,tend parent mar#up using tag <Cicket:extend>. >ote The content of the <Cicket:head> tag is added to the header section once per component class. -n other words* if we add multiple instances of the same panel to a page* the <head> tag will be populated Hust once with the content of <Cicket:head>. Warning The <Cicket:head> tag is ideal if we want to define small in-line bloc#s of !"" or Ka.a"cript. 9owe.er 2ic#et pro.ides also a more sophisticated techniEue to let components contribute to header section with in-line bloc#s and resource files li#e !"" or Ka.a"cript files. 2e will see this techniEue later in chapter 1$.
$%
Ka.a code(
/abel label 8 neC /abel79helloWorld9, QDello WorldFN:1 label.set*ender.od"'nl"7true:1 add7label:1
As you can see the <di4> tag used for component Label is not present in the final mar#up.
2ic#et comes with a nice utility tag called <Cicket:enclosure> that automatically hides those decorating elements if the related data .alue is not .isible. All we ha.e to do is to put the in.ol.ed mar#up inside this tag. Applying <Cicket:enclosure> to the pre.ious e,ample we get the following mar#up(
<Cicket:enclosure> <label>Total amount: </label><span Cicket:id89total5mount9></span> </Cicket:enclosure>
ow if component total5mount is not .isible* its description (Total amount:) will be automatically hidden. -f we ha.e more then a 2ic#et component inside <Cicket:enclosure> we can use child attribute to specify which component will control the o.erall .isibility(
<Cicket:enclosure child89total5mount9> <label>Total amount: </label><span Cicket:id89total5mount9></span><br/> <label>(xpected deli4er" date: </label><span Cicket:id89deli4+ate9></span> </Cicket:enclosure>
$1
child
The <Cicket:bod"/> tag used in the e,ample abo.e is used to indicate where the body of the tag will be placed inside border mar#up. ow if we attached this border to the following tag
<span Cicket:id89m".order9> bar </span>
1order can also contain children components which can be placed either inside its mar#up file or
Wicket free user guide
$4
inside its corresponding 9T<: tag. -n the first case children must be added to the border component with method add/o1order(Component... * while in the second case we must use the add (Component... method. The following e,ample illustrates both use cases( Border class(
public class ;".order extends .order public ;".order7)tring id: super7id:1 & &
Border mar#up(
<?xml 4ersion89@.A9 encoding890T3-B9?> <html xmlns89http://CCC.CR.org/@SSS/xhtml9 xmlns:Cicket89http://Cicket.apache.org9> <head></head> <bod"> <Cicket:border> <di4> <di4 Cicket:id89child;arkup9></di4> <Cicket:bod"/><br /> </di4> </Cicket:border> </bod"> </html>
Border tag(
<di4 Cicket:id89m".order9> <span Cicket:id89childTag9></span> </di4>
#.11 8ummar
-n this chapter we ha.e seen the tools pro.ided by 2ic#et to gain complete control o.er the generated 9T<:. 9owe.er we didnBt see yet how we can repeat a portion of 9T<: with 2ic#et. 2ith classic ser.er-side technologies li#e P9P or K"P we use loops (li#e while or 0or) inside our pages to achie.e this result.
11 nitiali<ation code should go inside component:s constructor or inside its method on nitiali<e 4we will see this method in the next chapter5
$$
To perform this tas# 2ic#et pro.ides a special-purpose family of components called repeaters and designed to repeat their mar#up body to display a set of items. But to fully understand how these components wor#* we must first learn more of 2ic#etBs basics. ThatBs why repeaters will be introduced later in chapter 11.
$3
9 Components lifecycle
-nitialiCing
+endering
+emo.ing
@nce a component has been remo.ed it can be added again to a container* but the initialiCation stage wonBt be e,ecuted again. >ote -f you read the Ka.aDoc of class Component you will find a more detailed description of component lifecycle. 9owe.er this description introduces some ad.anced topics we didnBt co.ered yet hence* to a.oid confusion* in this chapter some details ha.e been omitted and they will be co.ered later in the ne,t chapters. =or now you can consider Hust the simplified .ersion of the lifecycle described abo.e.
$5
9 Components lifecycle
component beha.ior during its lifecycle. -n the following table these methods are grouped according to the stage in which they are in.o#ed (and they are sorted by e,ecution order)(
C cle sta!e
!nitiali6ation
"nvolved met#ods
on-nitialiCe on!onfigure onBefore+ender on+ender on!omponentTag on!omponentTagBody onAfter+ender!hildren onAfter+ender on+emo.e
3endering
3emoving
(.#.1
+ethod on.onfigure
<ethod onCon0ig"re( has been introduced in order to pro.ide a good point to manage the component states such as its .isibility or enabled state. This method is called before the render phase starts. As stated in paragraph 3.1* is-isible and is'nabled are called multiple times when a page or a component is rendered* so itBs highly recommended not to directly o.erride these method* but rather to use onCon0ig"re to change component states. @n the contrary method on1e0ore)ender (see the ne,t paragraph) is not indicated for this tas# because it will not be in.o#ed if component .isibility is set to false.
(.#.2
+ethod on;efore3ender
The most important hoo# method of this stage is probably on1e0ore)ender( . This method is called before a component starts its rendering phase and it is our last chance to change its children hierarchy. -f we want add/remo.e children components this is the right place to do it.
14 7)7? support will be discussed in chapter 18$
$A
9 Components lifecycle
-n the ne,t e,ample (proHect %ife(ycleStages) we will create a page which alternately displays two different labels* swapping between them each time it is rendered(
public class Dome#age extends Web#age pri4ate /abel %irst/abel1 pri4ate /abel second/abel1 public Dome#age7: %irst/abel 8 neC /abel79label9, 93irst label9:1 second/abel 8 neC /abel79label9, 9)econd label9:1 add7%irst/abel:1 add7neC /ink79reload9: !'4erride public 4oid onClick7: & &:1 & !'4erride protected 4oid on.e%ore*ender7: i%7contains7%irst/abel, true:: replace7second/abel:1 else replace7%irst/abel:1 super.on.e%ore*ender7:1 & &
The code inside on1e0ore)ender( is Euite tri.ial as it Hust chec#s which label among 0irstLabel and secondLabel is currently inserted into the component hierarchy and it replaces the inserted label with the other one. This method is also responsible for in.o#ing children on1e0ore)ender( so if we decide to o.erride it we ha.e to call s"per.on1e0ore)ender( . 9owe.er* unli#e on2nitiali3e( , the call to superclass method should be placed at the end of methodBs body in order to affect childrenBs rendering with our custom code. Please note that in the e,ample abo.e we can trigger the rendering stage pressing =5 #ey or clic#ing on lin# FreloadG. Warning -f we forget to call superclass .ersion of methods on2nitiali3e( or on1e0ore)ender( * 2ic#et will throw an 2llegalState'(ception with the following message(
2a4a.lang.$llegal)tate(xception: org.apache.Cicket.Component has not been properl" initialiPed. )omething in the hierarch" o% <page class name> has not called super.on$nitialiPe7:/on.e%ore*ender7: in the o4erride o% on$nitialiPe7:/ on.e%ore*ender7: method
(.#.3
+ethod on.omponent*ag
$6
9 Components lifecycle
manipulated through its argument of type org.apache.wicket.mark"p.Component/ag. =or e,ample we can add/remo.e tag attributes with methods p"t(String ke,4 String !al"e and remo!e (String ke, * or we can e.en decide to change the tag or rename it with method setName(String (the following code is ta#en from proHect 2n(omponent!ag#0ample)( <ar#up code(
<head> <meta charset89ut%-B9 /> <title></title> </head> <bod"> <h@ Cicket:id89hello;essage9></h@> </bod">
Ka.a code(
public class Dome#age extends Web#age public Dome#age7: add7neC /abel79hello;essage9, 9Dello World9: !'4erride protected 4oid onComponentTag7ComponentTag tag: super.onComponentTag7tag:1 //Turn the h@ tag to a span tag.setName79span9:1 //5dd %ormatting st"le tag.put79st"le9, 9%ont-Ceight:bold9:1 & &:1 //... & &
0nerated mar#up(
<head> <meta charset89ut%-B9 /> <title></title> </head> <bod"> <span Cicket:id89hello;essage9 st"le89%ont-Ceight:bold9>Dello World</span> </bod">
Kust li#e we do with on2nitiali3e* if we decide to o.erride onComponent/ag we must remember to call the same method of the super class because also this class may also customiCe the tag. @.erriding onComponent/ag is perfectly fine if we ha.e to customiCe the tag of a specific component* but if we wanted to reuse the code across different components we should consider to use a beha.ior in place of this hoo# method. 2e ha.e already seen in paragraph 3.4 how to use beha.ior Attrib"teModi0ier to manipulate the tagBs attribute. -n paragraph 15.1 we will see that base class 1eha!ior offers also a callbac# method named onComponent/ag(Component/ag4 Component that can be used in place of the hoo# method onComponent/ag(Component/ag .
(.#.#
+ethods on.omponent*ag;od
$7
9 Components lifecycle
<ethod onComponent/ag1od,(Mark"pStream4 Component/ag is called to process the component tagBs body. Kust li#e onComponent/ag it ta#es as input a Component/ag parameter representing the component tag. -n addition* we also find a Mark"pStream parameter which represents the page mar#up stream that will be sent bac# to the client as response. onComponent/ag1od, can be used in combination with the ComponentBs method replace Component/ag1od, to render a custom body under specific conditions. =or e,ample (ta#en from proHect 2n(omponent!ag#0ample) we can display a brief description instead of the body if the label component is disabled(
public class Dome#age extends Web#age public Dome#age7: //... add7neC /abel79hello;essage9, 9Dello World9: !'4erride protected 4oid onComponentTag.od"7;arkup)tream markup)tream, ComponentTag tag: i%7Fis(nabled7:: replaceComponentTag.od"7markup)tream, tag, 97the component is disabled:9:1 else super.onComponentTag.od"7markup)tream, tag:1 & &:1 & &
ote that the original .ersion of onComponent/ag1od, is in.o#ed only when we want to preser.e the standard rendering mechanism for the tagBs body (in our e,ample this happens when the component is enabled).
(.- 8ummar
-n this chapter we ha.e seen which stages compose the lifecycle of 2ic#et components and which hoo# methods they pro.ide. @.erriding these methods we can dynamically modify the component hierarchy and we can enrich the beha.ior of our custom components.
$8
llustration 8$1% Page id at the end of @A0 -n this chapter we will use a re.ised .ersion of this e,ample proHect where the component hierarchy is modified inside the LinkBs onClick( method. This is necessary because 2ic#et creates a new page
3%
.ersion only if the page is modified before its method on1e0ore)ender( is in.o#ed. The code of the new home page is the following(
public class Dome#age extends Web#age pri4ate static %inal long serialOersion0$+ 8 @/1 pri4ate /abel %irst/abel1 pri4ate /abel second/abel1 public Dome#age7: %irst/abel 8 neC /abel79label9, 93irst label9:1 second/abel 8 neC /abel79label9, 9)econd label9:1 add7%irst/abel:1 add7neC /ink79reload9: !'4erride public 4oid onClick7: i%7get#age7:.contains7%irst/abel, true:: get#age7:.replace7second/abel:1 else get#age7:.replace7%irst/abel:1 & &:1 & &
ow if we run the new e,ample (proHect %ife(ycleStages"evisited) and we clic# on the F+eloadG button* a new page .ersion is created and the page id is increased by one(
llustration 8$*% pressing +Aeload- button causes page id to increment -f we press the bac# button the page .ersion pre.iously rendered (and serialiCed) will be retrie.ed (i.e. deserialiCed) and it will be used again to respond to our reEuest (and page id is decremented)(
llustration 8$1% pressing +Back- button we can go back to the previous page version 4page id is decremented5
31
>ote =or more details about page storing you can .isit the wi#i page at https(//cwi#i.apache.org/confluence/display/2-!MIT/PageN"torage. @n this page you can find which classes are in.ol.ed into page storing mechanism and how they wor# together. As we ha.e stated at the beginning of this chapter* page .ersions are stored using Ka.a serialiCation* therefore e.ery obHect referenced inside a page must be serialiCable 15. -n paragraph 8.A we will see how to o.ercome this limit and wor# with non-serialiCable obHects in our components using detachable 2ic#et models.
-.2.1
To retrie.e a specific page .ersion in our code we can use class org.apache.wicket. Page)e0erence by pro.iding its constructor with the corresponding page id(
//load page 4ersion Cith page id 8 R #age*e%erence page*e%erence 8 neC #age*e%erence7R:1 //load the related page instance #age page 8 page*e%erence.get#age7:1
-.2.2
-f for any reason we need to switch off .ersioning for a gi.en page* we can call its method set-ersioned(0alse .
-.2.3
Plugga=le seriali6ation
"tarting from .ersion 1.5 it is possible to choose which implementation of Ka.a serialiCation will be used by 2ic#et to store page .ersions. 2ic#et serialiCes pages using an implementation of interface org.apache.wicket.seriali3e.2Seriali3er. The default implementation is org.apache .wicket.seriali3e.5a!a..a!aSeriali3er and it uses the standard Ka.a serialiCation mechanism based on classes +b5ect+"tp"tStream and +b5ect2np"tStream. 9owe.er on -nternet we can find other interesting serialiCation libraries li#e Mryo1A which performs faster then the standard implementation. The serialiCer in use can be customiCed with the setSeriali3er(2Seriali3er method defined by setting interface org.apache.wicket.settings.2FrameworkSettings. 2e can access this interface inside the method init of the class Application using the getFrameworkSettings( method (
!'4erride public 4oid init7: super.init7:1 get3rameCork)ettings7:.set)erialiPer7"our)erialiPer:1 &
A erialiCer based on Mryo library is pro.ided by the 2ic#et"tuff proHect. 'ou can find more information on this proHect* as well as the instructions to use its modules* in Appendi, B.
15 t must implement standard interface #ava$io$'eriali<ation 16 http: code.google.com p kryo
34
-.2.#
Page caching
By default 2ic#et persists .ersions of pages into a session-relati.e file on dis#* but it uses a two-le.els cache to speed up this process. The first le.el of the cache uses a http session attribute called Fwic#et(persistentPage<anagerData->APP:-!AT-@ O A<I?G to store pages. The second le.el cache stores pages into application-scoped .ariables which are identified by a session id and a page id. The following picture is an o.er.iew of these two caching le.els(
llustration 8$3% 7n overview of Wicket cache structure The session-scoped cache is faster then the other memory le.els but it contains only the pages used to ser.e the last reEuest. 2ic#et allows us to set the ma,imum amount of memory allowed for the application-scoped cache and for the page store file. Both parameters can be configured .ia setting interface org.apache .wicket.settings.2StoreSettings. This interface pro.ides the setMa(Si3ePerSession(1,tes b,tes method to set the siCe for page store file. The 1,tes parameter is the ma,imum siCe allowed for this file(
!'4erride public 4oid init7: super.init7:1 get)tore)ettings7:.set;ax)iPe#er)ession7."tes.kilob"tes7TAA::1 &
!lass org.apache.wicket."til.lang.1,tes is an utility class pro.ided by 2ic#et to e,press siCe in bytes (for further details refer to the Ka.aDoc). =or the second le.el cache we can use the set2nmemor,CacheSi3e(int inmemor,CacheSi3e method. The integer parameter is the ma,imum number of page instances that will be sa.ed into application-scoped cache(
!'4erride public 4oid init7: super.init7:1 get)tore)ettings7:.set$nmemor"Cache)iPe7TA:1 &
-.2.(
Page expiration
Page instances are not #ept in the user session fore.er. They can be discarded when the limit set with the setMa(Si3ePerSession method is reached or (more often) when user session e,pires. 2hen we as# 2ic#et for a page id corresponding to a page instance remo.ed from the session* we bump into a
3$
This error page can be customiCed with the setPage'(pired'rrorPage method of the org.apache.wicket.settings.2ApplicationSettings interface(
!'4erride public 4oid init7: super.init7:1 get5pplication)ettings7:.set#age(xpired(rror#age7Custom(xpired(rror#age.class:1 &
The page class pro.ided as custom error page must ha.e a public constructor with no argument or a constructor that ta#es as input a single PageParameters argument (the page must be bookmarkable as described in paragraph 7.1.1).
33
!'4erride public 4oid component7Component component, $Oisit<Ooid> arg@: i%7Fcomponent.is)tateless7:: )"stem.out.println79Component 9 > component.get$d7: > 9 is not stateless9:1 & &:1 &
Alternati.ely* we could use the StatelessComponent utility annotation along with the StatelessChecker class (they are both in pac#age org.apache.wicket.de!"tils. stateless). StatelessChecker will throw an 2llegalArg"ment'(ception if a component annotated with StatelessComponent doesnBt respect the reEuirements for being stateless. To use StatelessComponent annotation we must first add the StatelessChecker to our application as a component render listener(
!'4erride public 4oid init7: super.init7:1 getComponent#ost'n.e%ore*ender/isteners7:.add7neC )tatelessChecker7::1 &
>ote <ost of the 2ic#etBs built-in components are stateful* hence they can not be used with a stateless page. 9owe.er some of them ha.e also a stateless .ersion which can be adopted when we need to #eep a page stateless. -n the rest of the guide we will point out when a built-in component comes also with a stateless .ersion. A page can be also e,plicitly declared as stateless setting the appropriate flag to true with the setStatelessHint(tr"e method. This method will not pre.ent us from .iolating the reEuirements for a stateless page* but if we do so we will get the following warning log message(
#age I<page class>I is not stateless because o% component Cith path I<component path>I
-.# 8ummar
-n this chapter we ha.e seen how page instances are managed by 2ic#et. 2e ha.e learnt that pages can be di.ided into two families( stateless and stateful pages. Mnowing the difference between the two types of pages is important to build the right page for a gi.en tas#. 9owe.er* to complete the discussion about stateless pages we still ha.e to deal with two topics we ha.e Hust outlined in this chapter( class PageParameters and bookmarkable pages. The first part of chapter 7 will co.er these missing topics.
35
'.2
The )e6"est and )esponse classes are located in pac#age org.apache.wicket.re6"est and they pro.ide an abstraction of the concrete reEuest and response used by our web application. Both classes are declared as abstract but if our application class inherits from WebApplication it will use their sub classes Ser!letWeb)e6"est and Ser!letWeb)esponse* both of them located inside the pac#age org.apache.wicket.protocol.http.ser!let. Ser!letWeb)e6"est and Ser!letWeb)esponse wrap respecti.ely a HttpSer!let)e6"est and a HttpSer!let)esponse obHect. -f we need to access to these low-le.el obHects we can call )e6"estBs method getContainer)e6"est( and )esponseBs method getContainer )esponse( .
3A
ha.e seen in paragraph 4.4.4) and org.apache.wicket.Session in order to get the application and the session in use by the current thread. >ote The implementation of the get method ta#es ad.antage of the standard class 5a!a.lang./hreadLocal. "ee its Ka.aDoc for an introduction to local-thread .ariables. !lass org.apache.wicket.Component pro.ides the get)e6"estC,cle( con.enience method that internally in.o#es )e6"estC,cle.get( (
public %inal *eEuestC"cle get*eEuestC"cle7: return *eEuestC"cle.get7:1 &
method which is a
'.3.1
-n order to process a reEuest* )e6"estC,cle delegates the tas# to another entity which implements interface org.apache.wicket.re6"est.2)e6"estHandler. There are different implementations of this interface* each suited for a particular type of reEuested resource (a page to render* an AKAL reEuest* an &+: to an e,ternal page* etc...). To resol.e the right handler for a gi.en 9TTP reEuest* the )e6"estC,cle uses a set of obHects implementing the org.apache.wicket.re6"est.2)e6"estMapper interface. The mapping interface defines the getCompatibilit,Score()e6"est re6"est method which returns a score indicating how compatible the reEuest mapper is for the current reEuest. )e6"estC,cle will choose the mapper with the highest score and it will call its map)e6"est()e6"est re6"est method to get the proper handler for the gi.en reEuest. @nce )e6"estC,cle has resol.ed a reEuest handler* it in.o#es its method respond(2)e6"estC,cle re6"estC,cle to start reEuest processing. The following seEuence diagram recaps how a reEuest handler is resol.ed by the )e6"estC,cle(
36
De.elopers can create additional implementations of 2)e6"estMapper and add them to their application .ia the mo"nt(2)e6"estMapper mapper method of the WebApplication class. -n paragraph 7.A we will see how 2ic#et uses this method to add built-in mappers for mounted pages.
'.3.2
The )e6"estC,cle is also responsible for generating the &+: .alue (as CharSe6"ence) for the following entities( a page class* .ia the "rlFor(Class7C8 pageClass4 PageParameters parameters method an 2)e6"estHandler .ia the "rlFor(2)e6"estHandler handler method a )eso"rce)e0erence .ia the "rlFor()eso"rce)e0erence re0erence4 PageParameters params method (resource entities will be introduced in chapter 1$). The o.erloaded "rlFor method from abo.e also has a corresponding .ersion that returns an instance of org.apache.wicket.re6"est.9rl instead of a CharSe6"ence. This .ersion has the prefi, BmapB in its name (i.e. it has map9rlFor as full name).
'.3.3
+ethod set3esponsePage
The )e6"estC,cle class contains the implementation of the set)esponsePage method we use to redirect a user to a specific page (see paragraph 4.3). The namesa#e method of class org.apache.wicket. Component is Hust a con.enience method that internally in.o#es the actual implementation on current reEuest cycle(
public %inal 4oid set*esponse#age7%inal #age page: get*eEuestC"cle7:.set*esponse#age7page:1 &
'.3.#
The )e6"estC,cle comes with some hoo# methods which can be o.erridden to perform custom actions when reEuest handling reaches a specific stage. These methods are( on;egin3e1uestJ@: called when the )e6"estC,cle is about to start handling the reEuest. onDnd3e1uestJ@: called when the )e6"estC,cle has finished to handle the reEuest on5etachJ@: called after the reEuest handling has completed and the )e6"estC,cle is about to be detached from its thread. The default implementation of this method in.o#es detach( on the current session (the Session class will be shortly discussed in paragraph 6.3). <ethods on1e0ore)e6"est and on'nd)e6"est can be used if we need to e,ecute custom actions before and after business code is e,ecuted* such as opening a 9ibernate/KPA session and closing it when code has terminated. A more fle,ible way to interact with the reEuest processing is to use the listener interface org.apache.wicket.re6"est.c,cle.2)e6"estC,cleListener. -n addition to the three methods already seen for )e6"estC,cle* this interface offers further hoo#s into reEuest processing( on;egin3e1uestJ3e1uest. cle c cle@: 7see the description above8 onDnd3e1uestJ3e1uest. cle c cle@: 7see the description above8 on5etachJ3e1uest. cle c cle@: 7see the description above8 on3e1uestHandler3esolvedJ3e1uest. cle c cleG !3e1uestHandler handler@: called when an 2)e6"estHandler has been resol.ed. on3e1uestHandler8cheduledJ3e1uest. cle c cleG !3e1uestHandler handler@: called when an 2)e6"estHandler has been scheduled for e,ecution.
37
on3e1uestHandlerDxecutedJ3e1uest. cle c cleG !3e1uestHandler handler@: called when an 2)e6"estHandler has been e,ecuted. onDxceptionJ3e1uest. cle c cleG Dxception ex@: called when an e,ception has been thrown during reEuest processing. onDxception3e1uestHandler3esolvedJ3e1uest. cle rcG !3e1uestHandler rhG Dxception ex@: called when an 2)e6"estHandler has been resol.ed and will be used to handle an e,ception. on0rl+appedJ3e1uest. cle c cleG !3e1uestHandler handlerG 0rl url@: called when an &+: has been generated for an 2)e6"estHandler obHect.
To use the reEuest cycle listeners we must add them to our application which in turn will pass them to the new )e6"estC,cleBs instances created with create)e6"estC,cle method(
!'4erride public 4oid init7: super.init7:1 $*eEuestC"cle/istener m"/istener1 //listener initialiPation... get*eEuestC"cle/isteners7:.add7m"/istener: &
The get)e6"estC,cleListeners method returns an instance of class org.apache.wicket. re6"est.c,cle.)e6"estC,cleListenerCollection. This class is a sort of typed collection for 2)e6"estC,cleListener and it also implements the !omposite pattern17.
'.#.1
"imilar to the )e6"estC,cle* class org.apache.wicket.Session also offers support for listener entities. 2ith Session these entities must implement the callbac# interface org.apache.wicket. 2SessionListener which e,poses only the onCreated(Session session method. As you might guess from its name* this method is called when a new session is created. "ession listeners must be added to our application using a typed collection* Hust li#e we ha.e done before with reEuest cycle listeners(
!'4erride public 4oid init7:
38
super.init7:1 //listener initialiPation... $)ession/istener m"/istener1 //add a custom session listener get)ession/isteners7:.add7m"/istener: &
'.#.2
The Session class handles session attributes in much the same way as the standard interface 5a!a(.ser!let.http.HttpSession. The following methods are pro.ided to create* read and remo.e session attributes( setAttrib"te(String name4 Seriali3able !al"e ( creates an attribute identified by the gi.en name. -f the session already contains an attribute with the same name* the new .alue will replace the e,isting one. The .alue must be a serialiCable obHect. getAttrib"te(String name ( returns the .alue of the attribute identified by the gi.en name* or n"ll if the name does not correspond to any attribute. remo!eAttrib"te(String name ( remo.es the attribute identified by the gi.en name. By default class WebSession will use the underlying http session to store attributes. 2ic#et will automatically add a prefi, to the name of the attributes. This prefi, is returned by the WebApplicationBs method getSessionAttrib"tePre0i(( .
'.#.3
-f for any reason we need to directly access to the underlying HttpSession obHect* we can retrie.e it from the current reEuest with the following code(
Dttp)ession session 8 77)er4letWeb*eEuest:*eEuestC"cle.get7:.get*eEuest7:: .getContainer*eEuest7:.get)ession7:1
&sing the raw session obHect might be necessary if we ha.e to set a session attribute with a particular name without the prefi, added by 2ic#et. :etBs say for e,ample that we are wor#ing with Tomcat as web ser.er. @ne of the administrati.e tools pro.ided by Tomcat is a page listing all the acti.e user sessions of a gi.en web application(
5%
Tomcat allows us to set the .alues that will be displayed in columns F0uessed localeG and F0uessed &ser nameG. @ne possible way to do this is to use session attributes named F:ocaleG and Fuser ameG but we canBt create them .ia 2ic#etBs Session class because they would not ha.e e,actly the name reEuired by Tomcat. -nstead* we must use the raw HttpSession and set our attributes on it(
Dttp)ession session 8 77)er4letWeb*eEuest:*eEuestC"cle.get7:.get*eEuest7:: .getContainer*eEuest7:.get)ession7:1 session.set5ttribute79/ocale9, 9(N-/$)D9:1 session.set5ttribute79userName9, 9;r .ad-u"9:1
'.#.#
2ic#et doesnBt need to store data into user session as long as the user .isits only stateless pages. onetheless* e.en under these conditions* a temporary session obHect is created to process each reEuest but it is discarded at the end of the current reEuest. To #now if the current session is temporary* we can use the is/emporar,( method(
)ession.get7:.isTemporar"7:1
-f a session is not temporary (i.e. it is permanent)* itBs identified by an uniEue id which can be read calling the get2d( method. This .alue will be n"ll if the session is temporary. Although 2ic#et is able to automatically recogniCe when it needs to replace a temporary session with a permanent one* sometimes we may need to manually control this process to ma#e our initially temporary session permanent. To illustrate this possible scenario letBs consider proHect 9indSession#0ample where we ha.e a stateless home page which sets a session attribute inside its constructor and then it redirects the user to another page which displays with a label the session attribute pre.iously created. The code of the two pages is as follows( 9ome page(
public class Dome#age extends Web#age public Dome#age7%inal #age#arameters parameters: )ession.get7:.set5ttribute79username9, 9tomm"9:1 )ession. get 7:.bind7:1 set*esponse#age7+ispla")ession#arameter.class:1 & &
Target page(
public class +ispla")ession#arameter extends Web#age public +ispla")ession#arameter7: super7:1 add7neC /abel79username9, 7)tring: )ession.get7:.get5ttribute79username9:::1 & &
Again* we #ept page logic .ery simple to not o.er-bloat the e,ample with unnecessary code. -n the snippet abo.e we ha.e also bolded SessionBs bind( method which con.erts temporary session into
51
a permanent one. -f the home page has not in.o#ed this method* the session with its attribute would ha.e been discarded at the end of the reEuest and the page :ispla,SessionParameter would ha.e displayed an empty .alue in its label.
'.#.(
@nce a user has finished using our web application* she must be able to log out and clean any session data. To be sure that a permanent session will be discarded at the end of the current reEuest* class Session pro.ides the in!alidate( method. -f we want to immediately in.alidate a gi.en session without waiting for the current reEuest to complete* we can in.o#e the in!alidateNow( method. Warning +emember that in!alidateNow( will immediately remo.e any instance of components (and pages) from the session* meaning that once we ha.e called this method we wonBt be able to wor# with them for the rest of the reEuest process.
54
//+o some stu%%... /** * ;etadata ke" de%inition */ public static ;eta+ata6e"<Conncetion> connection6e" 8 neC ;eta+ata6e"<Conncetion>7: &1 /** * 5pplicationIs initialiPation */ !'4erride public 4oid init7: super.init7:1 Connection connection1 //connection initialiPation... set;eta+ata7connection6e", connection:1 //+o some other stu%%.. & &
"ince Meta:ata;e,7/8 class is declared as abstract* we must implement it with a subclass or with an anonymous class (li#e we did in the e,ample abo.e).
'.- 8ummar
-n this chapter we had a loo# at how 2ic#et internally handles a web reEuest. I.en if most of the time we wonBt need to customiCe this internal process* #nowing how it wor#s is essential to use the framewor# at 1%%P. Intities li#e Application and Session will come in handy again when we will tac#le the topic of security in chapter 17.
5$
2.1 PageParameters
A common practice in web de.elopment is to pass data to a page using 5uery string parameters (li#e ? paramName1=paramValu1¶mName2=paramValue2...). 2ic#et offers a more fle,ible and obHect oriented way to do this with models (we will see them in the ne,t chapter). 9owe.er* e.en if we are using 2ic#et* we still need to use Euery string parameters to e,change data with other -nternet-based ser.ices. !onsider for e,ample a classic confirmation page which is lin#ed inside an email to let users confirm important actions li#e password changing or the subscription to a mailing list. This #ind of page usually e,pects to recei.e a Euery string parameter containing the id of the action to confirm. Query string parameters can also be referred to as named parameters. -n 2ic#et they are handled with class org.apache.wicket.re6"est.mapper.parameter.PageParameters. "ince named parameters are basically name-.alue pairs* PageParameters wor#s in much the same way as Ka.a Map pro.iding two methods to create/modify a parameter ( add(String name4 +b5ect !al"e and set(String name4 +b5ect !al"e )* one method to remo.e an e,isting parameter (remo!e(String name ) and one to retrie.e the .alue of a gi.en parameter ( get(String name ) . 9ere is a snippet to illustrate the usage of PageParameters(
#age#arameters page#arameters 8 neC #age#arameters7:1 //add a couple o% parameters page#arameters.add79name9, 9Lohn9:1 page#arameters.add79age9, =B:1 //retrie4e the 4alue o% IageI parameter page#arameters.get79age9:1
ow that we ha.e seen how to wor# with page parameters* letBs see how to use them with our pages.
2.1.1
Base class Page comes with a constructor which ta#es as input a PageParameters instance. -f we use this superclass constructor in our page* PageParameters will be used to build the page &+: and it can be retrie.ed at a later time with the PageBs getPageParameters( method. -n the following e,ample ta#en from the PageParameters#0ample proHect we ha.e a home page with a lin# to a second page that uses a .ersion of set)esponsePage method that ta#es as input also a PageParameters to build the target page (named PageWithParameters). The code for the lin# and for the target page is the following(
Wicket free user guide
53
:in# code(
add7neC /ink79pageWith$ndex#aram9: !'4erride public 4oid onClick7: #age#arameters page#arameters 8 neC #age#arameters7:1 page#arameters.add79%oo9, 9%oo9:1 page#arameters.add79bar9, 9bar9:1 set*esponse#age7#ageWith#arameters.class, page#arameters:1 & &:1
The code is Euite straightforward and itRs more interesting to loo# at the &+: generated for the target page(
<app root>/#age#arameters(xample/Cicket/bookmarkable/org.CicketTutorial.#ageWith#arameters ?%oo8%ooUbar8bar
At first glance the &+: abo.e could seem a little weird* e,cept for the last part which contains the two named parameters used to build the target page. The reason for this FstrangeG &+: is that* as we e,plained in paragraph A.4.5* when a page is instantiated using a constructor with no argument or using a constructor that accepts only a PageParameters* 2ic#et will try to generate a static &+: for it* with no session-relati.e informations. This #ind of &+: is called bookmarkable because it can be sa.ed by the users as a boo#mar# and accessed at a later time. A boo#mar#able &+: is composed by a fi,ed prefi, (which by default is bookmarkable) and the Eualified name of the page class ( org.wicket/"torial.PageWithParameters in our e,ample). "egment Cicket is another fi,ed prefi, added by default during &+: generation. -n paragraph 7.A.3 we will see how to customiCe fi,ed prefi,es with a custom implementation of 2MapperConte(t interface.
2.1.2
!ndexed parameters
Besides named parameters* 2ic#et also supports inde0ed parameters. These #inds of parameters are rendered as &+: segments placed before named parameters. :etBs consider for e,ample the following &+:(
<application path>/%oo/bar?@UbaP8baP
The &+: abo.e contains two inde,ed parameters ( %oo and bar) and a Euery string consisting of the page id and a named parameter (baP). Kust li#e named parameters also inde,ed parameters are
55
handled by the PageParameters class. The methods pro.ided by PageParameters for inde,ed parameters are set(int inde(4 +b5ect ob5ect (to add/modify a parameter)* remo!e(int inde( (to remo.e a parameter) and get(int inde( (to read a parameter). As their name suggests* inde,ed parameters are identified by a numeric inde, and they are rendered following the order in which they ha.e been added to the PageParameters. The following is an e,ample of inde,ed parameters(
#age#arameters page#arameters 8 neC #age#arameters7:1 //add a couple o% parameters page#arameters.set7A, 9%oo9:1 page#arameters.set7@, 9bar9:1 //retrie4e the 4alue o% the second parameter 79bar9: page#arameters.get7@:1
ProHect PageParameters#0ample comes also with a lin# to a page with both inde,ed parameters and a named parameter(
add7neC /ink79pageWithNamed$ndex#aram9: !'4erride public 4oid onClick7: #age#arameters page#arameters 8 neC #age#arameters7:1 page#arameters.set7A, 9%oo9:1 page#arameters.set7@, 9bar9:1 page#arameters.add79baP9, 9baP9:1 set*esponse#age7#ageWith#arameters.class, page#arameters:1 & &:1
The &+: generated for the lin#ed page (PageWithParameters) is the one seen at the beginning of the paragraph.
The specific purpose of this component is to pro.ide an anchor to a boo#mar#able page* hence we donBt ha.e to implement any abstract method li#e we do with Link component.
5A
<F+'CTG#( html> <html xmlns:Cicket89http://Cicket.apache.org9> <head> <meta charset89ut%-B9 /> <title>5pache Wicket Vuickstart</title> </head> <bod"> <di4 id89bd9> <Cicket:link> <a hre%89Dome#age.html9>Dome#age</a><br/> <a hre%89another#ackage/)ub#ackage#age.html9>)ub#ackage#age</a> </Cicket:link> </di4> </bod"> </html>
The #ey part of the mar#up abo.e is the hre% attribute which must contain the pac#age-relati.e path to a page. The home page is inside pac#age org.wicket/"torial which in turns contains the sub pac#age anotherPackage. This pac#age hierarchy is reflected by the hre% attributes( in the first anchor we ha.e a lin# to the home page itself while the second anchor points to page S"bPackagePage which is placed into sub pac#age anotherPackage. Absolute paths are supported as well and we can use them if we want to specify the full pac#age of a gi.en page. =or e,ample the lin# to S"bPackagePage could ha.e been written in the following (more .erbose) way (
<a hre%89/org/CicketTutorial/another#ackage/)ub#ackage#age.html9>)ub#ackage#age</a>
-f we ta#e a loo# also at the mar#up of S"bPackagePage we can see that it contains a lin# to the home page which uses the parent directory selector (relati.e path)(
<F+'CTG#( html> <html xmlns:Cicket89http://Cicket.apache.org9> <head> <meta charset89ut%-B9 /> <title>5pache Wicket Vuickstart</title> </head> <bod"> <di4 id89bd9> <Cicket:link> <a hre%89../Dome#age.html9>Dome#age</a><br/> <a hre%89)ub#ackage#age.html9>)ub#ackage#age</a> </Cicket:link> </di4> </bod"> </html>
Please note that any lin# to the current page (a#a self link) is disabled. =or e,ample in the home page the self lin# is rendered li#e this(
<span><em>Dome#age</em></span
56
The mar#up used to render disabled lin#s can be customiCed using the mar#up settings (interface 2Mark"pSettings) a.ailable in the application class(
!'4erride public 4oid init7: super.init7:1 //Crap disabled links Cith <b> tag get;arkup)ettings7:.set+e%ault.e%ore+isabled/ink79<b>9:1 get;arkup)ettings7:.set+e%ault5%ter+isabled/ink79</b>9:1 &
The purpose of <Cicket:link> tag is not limited to Hust simplifying the usage of boo#mar#able pages. As we we will see in chapter 1$* this tag can also be adopted to manage web resources li#e pictures* !"" files* Ka.a"cript files and so on.
Ka.a code(
#erson person 8 neC #erson79Lohn9, 9)mith9:1 )tring %ullName 8 person.get3ullName7:1 //)pace characters must be replaced b" character I>I )tring googleVuer" 8 9http://CCC.google.com/search?E89 > %ullName.replace79 9, 9>9:1 add7neC (xternal/ink79external)ite9, googleVuer"::1
0enerated anchor(
<a hre%89http://CCC.google.com/search?E8Lohn>)mith9>)earch me on -oogleF</a>
-f we need to specify a dynamic .alue for the te,t inside the anchor* we can pass it as an additional constructor parameter( 9tml(
<a Cicket:id89external)ite9>/abel goes here...</a>
Ka.a code(
#erson person 8 neC #erson79Lohn9, 9)mith9:1 )tring %ullName 8 person.get3ullName7:1 )tring googleVuer" 8 9http://CCC.google.com/search?E89 > %ullName.replace79 9, 9>9:1 )tring link/abel 8 9)earch I9 > %ullName > 9I on -oogle.91 add7neC (xternal/ink79external)ite9, googleVuer", link/abel::1
57
0enerated anchor(
<a hre%89http://CCC.google.com/search?E8Lohn>)mith9>)earch ILohn )mithI on -oogle.</a>
The printed .alue will always be Cero because a new instance of the page is used e.ery time the user clic#s on the statelessLink lin#.
20 http%CCen$wikipedia$orgCwikiC'earchDengineDoptimi<ation
58
2.-.1
2ith 2ic#et we can mount a page to a gi.en path in much the same way as we map a ser.let filer to a desired path inside file web.0ml (see page 8). &sing mo"ntPage(String path4 Class 7/8 pageClass method of the WepApplication class we tell 2ic#et to respond with a new instance of pageClass whene.er a user na.igates to the gi.en path. -n the application class of the proHect 4ountedPages#0ample we mount Mo"ntedPage to the 9<pageMo"nt= path(
!'4erride public 4oid init7: super.init7:1 mount#age79/page;ount9, ;ounted#age.class:1 //'ther initialiPation code... &
The path pro.ided to mo"ntPage will be used to generate the &+: for any page of the specified class(
//it Cill return 9/page;ount9 *eEuestC"cle.get7:.url3or7;ounted#age.class:1
&nder the hood the mo"ntPage method mounts an instance of the reEuest mapper org.apache. wicket.re6"est.mapper.Mo"ntedMapper configured for the gi.en path(
public %inal <T extends #age> 4oid mount#age7%inal )tring path,%inal Class<T> pageClass: mount7neC ;ounted;apper7path, pageClass::1 &
+eEuest mappers and the ApplicationBs method chapter (paragraph 6.$.1). 2.-.2
mount
The path specified for mounted pages can contain dynamic segments which are populated with the .alues of the named parameters used to build the page. These segments are declared using special segments called parameter placeholders. !onsider the path used in the following e,ample(
mount#age79/page;ount/W %oo&/other)egm9, ;ounted#ageWith#laceholder.class:1
The path used abo.e is composed by three segments( the first and the last are fi,ed while the second will be replaced by the .alue of the named parameter %oo that must be pro.ided when the page Mo"ntedPageWithPlaceholder is instantiated(
Ka.a code(
#age#arameters page#arameters 8 neC #age#arameters7:1 page#arameters.add79%oo9, 9%oo9:1 set*esponse#age7;ounted#ageWith#laceholder.class, page#arameters:1
A%
0enerated &+:(
<5pplication path>/page;ount/%oo/other)egm
@n the contrary if we manually insert an &+: li#e B <Ceb app path>/page;ount/bar/other)egmI* we can read .alue BbarB retrie.ing the named parameter %oo inside our page. Place holders can be declared as optional using the BSB character in place of BTB(
mount#age79/page;ount/X %oo&/other)egm9, ;ounted#age'ptional#laceholder.class:1
-f the named parameter for an optional placeholder is missing* the corresponding segment is remo.ed from the final &+:(
Ka.a code(
#age#arameters page#arameters 8 neC #age#arameters7:1 set*esponse#age7;ounted#ageWith#laceholder.class, page#arameters:1
0enerated &+:(
<5pplication path>/page;ount/other)egm
2.-.3
+ounting a package
-n addition to mounting a single page* 2ic#et allows to mount all of the pages inside a pac#age to a gi.en path. <ethod mo"ntPackage(String path4 Class7/8 pageClass of class WepApplication will mount e.ery page inside pageClassBs pac#age to the specified path. The resulting &+: for pac#age-mounted pages will ha.e the following structure(
<5pplication path>/mounted#ath/<#ageClassName>Hoptional Euer" stringJ
=or e,ample in the 4ountedPages#0ample proHect we ha.e mounted all pages inside the subpac#age org.t"torialWicket.s"bPackage with this line of code(
mount#ackage79/mount#ackage9, )tate%ul#ackage;ount.class:1
State0"lPackageMo"nt is one of the pages placed into the desired pac#age and its &+: will be(
<5pplication path>/mount#ackage/)tate%ul#ackage;ount?@
"imilarly to what is done by the mo"ntPage method* the implementation of the mo"ntPackage method mounts an instance of org.apache.wicket.re6"est.mapper.PackageMapper to the gi.en path.
2.-.#
-nterface org.apache.wicket.re6"est.mapper.2MapperConte(t is used by reEuest mappers to create new page instances and to retrie.e static &+: segments used to build and parse page &+:s. 9ere is the list of these segments( )amespace( itBs the first &+: segment of not-mounted pages. By default its .alue is Cicket. Identifier for non bookmarkable :"%s ( itBs the segment that identifies non boo#mar#able pages.
Wicket free user guide
A1
By default its .alue is page. Identifier for bookmarkable :"%s( itBs the segment that identifies boo#mar#able pages. By default its .alue is bookmarkable (as we ha.e seen before in paragraph 7.1.1). Identifier for resources( itBs the segment that identifies 2ic#et resources. -ts default .alue is resources. The topic of resource management will be co.ered in chapter 1$.
2MapperConte(t pro.ides a getter method for any segment listed abo.e. By default 2ic#et uses class org.apache.wicket.:e0a"ltMapperConte(t as mapper conte,t. ProHect (ustom4apper(onte0t is an e,ample of customiCation of mapper conte,t where we use index as identifier for non-boo#mar#able pages and static0*/ as identifier for boo#mar#able pages. -n this proHect* instead of implementing our mapper conte,t from scratch* we used :e0a"ltMapperConte(t as base class o.erriding Hust the two methods we need to achie.e the desired result (get1ookmarkable2denti0ier( and getPage2denti0ier( ). The final implementation is the following(
public class Custom;apperContext extends +e%ault;apperContext !'4erride public )tring get.ookmarkable$denti%ier7: return 9static0*/91 & !'4erride public )tring get#age$denti%ier7: return 9index91 & &
ow to use a custom mapper conte,t in our application we must o.erride the newMapperConte(t( method declared in the Application class and ma#e it return our custom implementation of 2MapperConte(t(
!'4erride protected $;apperContext neC;apperContext7: return neC Custom;apperContext7:1 &
2.-.(
"ome reEuest mappers (li#e Mo"ntedMapper and PackageMapper) can delegate page parameters encoding/decoding to interface org.apache.wicket.re6"est.mapper.parameter.2Page Parameters'ncoder. This entity e,poses two methods( encodePageParameters( and decodePageParameters( ( the first one is in.o#ed to encode page parameters into an &+: while the second one e,tracts parameters from the &+:. 2ic#et comes with a built-in implementation of this interface which encodes named page parameters as &+: segments using the following patter( /paramName1/paramValue1/paramName2/param Value2... This built-in encoder is org.apache.wicket.re6"est.mapper.parameter.UrlPathPage ParametersEncoder class. -n the PageParameters#ncoder#0ample proHect we ha.e manually mounted a Mo"ntedMapper that ta#es as input also an 9rlPathPageParameters'ncoder(
!'4erride
A4
public 4oid init7: super.init7:1 mount7neC ;ounted;apper79/mounted#ath9, ;ounted#age.class, neC 0rl#ath#age#arameters(ncoder7:::1 &
The home page of the proHect contains Hust a lin# to the Mo"ntedPage web page. The code of the lin# and the resulting page &+: are(
:in# code(
add7neC /ink79mounted#age9: !'4erride public 4oid onClick7: #age#arameters page#arameters 8 neC #age#arameters7:1 page#arameters.add79%oo9, 9%oo9:1 page#arameters.add79bar9, 9bar9:1 set*esponse#age7;ounted#age.class, page#arameters:1 & &:1
0enerated &+:(
<5pplication path>/mounted#ath/%oo/%oo/bar/bar?@
2.-.-
"ometimes &+:s are a doubleUedged sword for our site because they can e,pose too many details about the internal structure of our web application and malicious users could e,ploit them to perform a cross-site reEuest forgery41. To a.oid this #ind of security threat we can use the Cr,ptoMapper reEuest mapper which wraps an e,isting mapper and encrypts the original &+: producing a single encrypted segment(
llustration =$1% 7n @A0 with the encrypted segment Typically* Cr,ptoMapper is registered into a 2ic#et application as the root reEuest mapper wrapping the default one(
!'4erride public 4oid init7: super.init7:1 set*oot*eEuest;apper7neC Cr"pto;apper7get*oot*eEuest;apper7:, this::1 //pages and resources must be mounted a%ter Ce ha4e set Cr"pto;apper mount#age79/%oo/9, Dome#age.class:1
A$
&
As pointed out in the code abo.e* pages and resources must be mounted after ha.ing set Cr,ptoMapper as root mapper* otherwise the mounted paths will not wor#.
2.' 8ummar
:in#s and &+:s are not tri.ial topics as they may seem and in 2ic#et they are strictly interconnected. De.elopers must choose the right trade-off between producing structured &+:s and a.oiding to ma#e them .erbose and .ulnerable. -n this chapter we ha.e e,plored the tools pro.ided by 2ic#et to control how &+:s are generated. 2e ha.e started with static &+:s for boo#mar#able pages and we ha.e seen how to pass parameters to target pages with PageParameters. -n the second part of the chapter we focused on mounting pages to a specific path and on controlling how parameters are encoded by 2ic#et. =inally* we ha.e also seen how to encrypt &+:s to pre.ent security .ulnerabilities.
A3
llustration >$1% @/0 class diagram of Component and /odel The 2Model interface defines Hust the methods needed to get and set a data obHect ( get+b5ect( and set+b5ect( )* decoupling components from concrete details about the persistence strategy adopted for data. -n addition* the le.el of indirection introduced by models allows access data obHect only when it is really needed (for e,ample during the rendering phase) and not earlier when it may not be ready to be used. Any component can get/set its model as well as its data obHect using the 3 public shortcut methods listed in the class diagram abo.e. The two methods onModelChanged( and onModelChanging( are triggered by 2ic#et each time a model is modified( the first one is called after the model has been changed* the second one Hust before the change occurs. -n the e,amples seen so far we ha.e wor#ed with Label component using its constructor which ta#es as input two string parameters* the component id and the te,t to display(
add7neC /abel79hello;essage9, 9Dello WicketWorldF9::1
This constructor internally builds a model which wraps the second string parameter. ThatBs why we didnBt mention label model in the pre.ious e,amples. 9ere is the code of this constructor(
public /abel7%inal )tring id, )tring label:
22 Wicket models have nothing to do with the model we talked about in paragraph 1$*,, 23 (or an introduction to (acade pattern see http: en.wikipedia.org wiki ;acade1pattern
A5
!lass org.apache.wicket.model.Model is a basic implementation of 2Model. -t can wrap any obHect that implements the interface 5a!a.io.Seriali3able. The reason of this constraint o.er data obHect is that this model is stored in the web session* and we #now from chapter A that data are stored into session using serialiCation. >ote -n general* 2ic#et models support a detaching capability that allows us to wor# also with non-serialiCable obHects as data model. 2e will see the detaching mechanism later in this chapter. Kust li#e any other 2ic#et components* Label pro.ides a constructor that ta#es as input the component id and the model to use with the component. &sing this constructor the pre.ious e,ample becomes(
add7neC /abel79hello;essage9, neC ;odel<)tring>79Dello WicketWorldF9:::1
>ote The Model class comes with a bunch of factory methods that ma#es it easier to build new model instances. =or e,ample the o0(/ ob5ect method creates a new instance of Model which wraps any +b5ect instance inside it. "o instead of writing
neC ;odel<)tring>79Dello WicketWorldF9:
we can write
;odel.o%79Dello WicketWorldF9:
-f the data obHect is a List* a Map or a Set we can use similar methods called o0List* o0Map and o0Set. =rom now on we will use these factory methods in our e,amples. -tBs Euite clear that if our Label must display a static te,t it doesnBt ma#e much sense to build a model by hand li#e we did in the last code e,ample. 9owe.er is not unusual to ha.e a Label that must display a dynamic .alue* li#e the input pro.ided by a user or a .alue read from a database. 2ic#et models are designed to sol.e these #inds of problems. :etBs say we need a label to display the current time stamp each time a page is rendered. 2e can implement a custom model which returns a new :ate instance when the get+b5ect( method is called(
$;odel time)tamp;odel 8 neC ;odel<)tring>7: !'4erride public )tring get'b2ect7: return neC +ate7:.to)tring7:1 & &1
AA
I.en if sometimes writing a custom model could be a good choice to sol.e a specific problem* 2ic#et already pro.ides a set of 2Model implementations which should fit most of our needs. -n the ne,t paragraph we will see a couple of models that allow us to easily integrate Ka.aBeans with our web applications and in particular with our forms. >ote By default class Component escapes 9T<: sensiti.e characters (li#e B 7B* B8B or B>B) from the te,tual representation of its model obHect. The term BescapeB means that these characters will be replaced with their corresponding 9T<: entity 43 (for e,ample B7B becomes B>lt? B). This is done for security reasons as a malicious user could attempt to inHect mar#up or Ka.a"cript into our pages. -f we want to display the raw content stored inside a model* we can tell the Component class not to escape characters by calling the set'scape ModelStrings(0alse method.
A6
&
4.2.1
Propert +odel
:etBs say we want to display the name field of a Person instance with a label. 2e could* of course* use the Model class li#e we did in the pre.ious e,ample* obtaining something li#e this(
#erson person 8 neC #erson7:1 //load personIs data... /abel label 8 neC /abel79name9, neC ;odel7person.getName7:::1
9owe.er this solution has a huge drawbac#( the te,t displayed by the label will be static and if we change the .alue of the field* the label wonBt update its content. -nstead* to always display the current .alue of a class field* we should use the org.apache.wicket.model.Propert,Model model class(
#erson person 8 neC #erson7:1 //load personIs data... /abel label 8 neC /abel79name9, neC #ropert";odel7person, 9name9::1
Propert,Model has Hust one constructor with two parameters( the model obHect ( person in our e,ample) and the name of the property we want to read/write ( 9name9 in our e,ample). This last parameter is called property e0pression. -nternally* methods get2b-ect/set2b-ect use property e,pression to get/set propertyBs .alue. To resol.e class properties Propert,Model uses class org.apache.wicket."til.lang.Propert, )esol!er which can access any #ind of property* pri.ate fields included. Kust li#e Ka.a language* property e,pressions support dotted notation to select sub properties. "o if we want to display the name of the PersonBs spouse we can write(
/abel label 8 neC /abel79spouseName9, neC #ropert";odel7person, 9spouse.name9::1
>ote Propert,Model is null-safe* which means we donBt ha.e to worry if property e,pression includes a null .alue in its path. -f such a .alue is encountered* an empty string will be returned. -f property is an array or a List* we can specify an inde, after its name. =or e,ample* to display the name of the first child of a Person we can write the following property e,pression(
/abel label 8 neC /abel79%irstChildName9, neC #ropert";odel7person, 9children.A.name9::1
-nde,es and map #eyes can be also specified using sEuared brac#ets li#e FchildrenV%W.nameG or Fmap=ieldV#eyW.subfieldG.
4.2.2
!lass org.apache.wicket.model.Compo"ndPropert,Model is a particular #ind of model which is usually used in conHunction with another 2ic#et feature called model inheritance. 2ith this feature* when a component needs to use a model but no one has been assigned to it* it will
A7
search through the whole container hierarchy for a parent with an inheritable model. -nheritable models are those which implement interface org.apache.wicket.model.2Component2nheritedModel and Compo"ndPropert,Model is one of them. @nce a Compo"ndPropert,Model has been inherited by a component* it will beha.e Hust li#e a Propert,Model using the id of the component as property e,pression. As a conseEuence* to ma#e the most of Compo"ndPropert,Model we must assign it to one of the container of a gi.en component* rather than directly to the component itself. =or e,ample if we use Compo"ndPropert,Model with the pre.ious e,ample (display spouseBs name)* the code would become li#e this(
//set Compound#ropert";odel as model %or the container o% the label set+e%ault;odel7neC Compound#ropert";odel7person::1 /abel label 8 neC /abel79spouse.name9:1 add7label:1
ote that now the id of the label is eEual to the property e,pression pre.iously used with Propert,Model. ow as a further e,ample letBs say we want to e,tend the code abo.e to display all of the main informations of a person (name* surname* address and email). All we ha.e to do is to add one label for e.ery additional information using the relati.e property e,pression as component id(
//Create a person named ILohn )mithI #erson person 8 neC #erson79Lohn9, 9)mith9:1 set+e%ault;odel7neC Compound#ropert";odel7person::1 add7neC /abel79name9::1 add7neC /abel79surname9::1 add7neC /abel79address9::1 add7neC /abel79email9::1 add7neC /abel79spouse.name9::1
Compo"ndPropert,Model can sa.e us a lot of boring coding if we choose the id of components according to properties name. 9owe.er itBs also possible to use this type of model e.en if the id of a component does not correspond to a .alid property e,pression. The method bind(String propert, allows to create a property model from a gi.en Compo"ndPropert,Model using the pro.ided parameter as property e,pression. =or e,ample if we want to display the spouseBs name in a label ha.ing 9x"P9 as id* we can write the following code(
//Create a person named ILohn )mithI #erson person 8 neC #erson79Lohn9, 9)mith9:1 Compound#ropert";odel compound;odel1 set+e%ault;odel7compound;odel 8 neC Compound#ropert";odel7person::1 add7neC /abel79x"P9, compound;odel.bind79spouse.name9:::1
Compo"ndPropert,Model are particularly useful when used in combination with 2ic#et forms* as we will see in the ne,t paragraph.
A8
>ote Model is referred to as static model because the result of its method get+b5ect is fi,ed an it is not dynamically e.aluated each time the method is called. -n contrast* models li#e Propert,Model and Compo"ndPropert, Model are called dynamic models.
Ka.a code(
3orm %orm 8 neC 3orm79%orm9: !'4erride protected 4oid on)ubmit7: )"stem.out.println793orm submitted.9:1 & &1 add7%orm:1
<ethod onS"bmit is called whene.er a form has been submitted and it can be o.erridden to perform custom actions. Please note that a 2ic#et form can be submitted using a standard 9T<: submit button which is not mapped to any component (i.e. it does not ha.e a Cicket:id attribute). -n the ne,t chapter we will continue to e,plore 2ic#et forms and we will see how to submit forms using special components which implement interface org.apache.wicket.mark"p.html.0orm. 2FormS"bmitter.
4.3.1
A form should contain some input fields (li#e te,t fields* chec# bo,es* radio buttons* drop-dpwn lists* te,t areas* etc...) to interact with users. 2ic#et pro.ides an abstraction for all these #inds of elements with component org.apache.wicket.mark"p.html.0orm.FormComponent(
llustration >$*% (ormComponent and some of the most common input components$ 7ll classes are under package org$apache$wicket$markup$html$form$
6%
The purpose of FormComponent is to store the corresponding user input into its model when the form is submitted. The form is responsible for mapping input .alues to the corresponding components* a.oiding us the burden of manually synchroniCing models with input fields and .ice .ersa.
4.3.2
,ogin form
As first e,ample of interaction between the form and its models* we will build a classic login form which as#s for username and password (proHect %ogin;orm). Warning The topic of security will be discussed later in chapter 17. The following form is for e,ample purposes only and is not suited for a real application. -f you need to use a login form you should consider to use component org.apache.wicket.a"throles.a"thentication.panel.Sign2nPanel shipped with 2ic#et. This form needs two te,t fields* one of which must be a password field. 2e should also use a label to display the result of login process45. =or the sa#e of simplicity* the login logic is all inside onS"bmit and is Euite tri.ial. The following is a possible implementation of our form(
public class /ogin3orm extends 3orm pri4ate Text3ield username3ield1 pri4ate #assCordText3ield passCord3ield1 pri4ate /abel login)tatus1 public /ogin3orm7)tring id: super7id:1 username3ield 8 neC Text3ield79username9, ;odel.o%799::1 passCord3ield 8 neC #assCordText3ield79passCord9, ;odel.o%799::1 login)tatus 8 neC /abel79login)tatus9, ;odel.o%799::1 add7username3ield:1 add7passCord3ield:1 add7login)tatus:1 & public %inal 4oid on)ubmit7: )tring username 8 7)tring:username3ield.get+e%ault;odel'b2ect7:1 )tring passCord 8 7)tring:passCord3ield.get+e%ault;odel'b2ect7:1 i%7username.eEuals79test9: UU passCord.eEuals79test9:: login)tatus.set+e%ault;odel'b2ect79CongratulationsF9:1 else login)tatus.set+e%ault;odel'b2ect79Wrong username or passCordF9:1 & &
-nside formBs constructor we build the three components used in the form and we assign them a model containing an empty string(
25 n chapter 1E we will see that Wicket offers a builtFin mechanism to display feedback messages to user$
61
username3ield 8 neC Text3ield79username9, ;odel.o%799::1 passCord3ield 8 neC #assCordText3ield79passCord9, ;odel.o%799::1 login)tatus 8 neC /abel79login)tatus9, ;odel.o%799::1
-f we donBt pro.ide a model to a form component* we will get the following e,ception on form submission(
2a4a.lang.$llegal)tate(xception: 5ttempt to set model ob2ect on null model o% component:
!omponent /e(tField corresponds to the standard te,t field* without any particular beha.ior or restriction on the allowed .alues. 2e must bind this component to the <input> tag with the attribute t"pe set to 9text9. Password/e(tField is a subtype of /e(tFiled and it must be used with an <input> tag with the attribute t"pe set to9passCord9. =or security reasons component Password/e(tField cleans its .alue at each reEuest* so it wil be always empty after the form has been rendered. By default Password/e(tField fields are re5uired, meaning that if we left them empty* the form wonBt be submitted (i.e. onS"bmit wonBt be called). !lass FormComponent pro.ides method set)e6"ired(boolean re6"ired to change this beha.ior. 4A -nside onS"bmit, to get/set model obHects we ha.e used shortcut methods set:e0a"ltModel+b5ect and get:e0a"ltModel+b5ect. Both methods are defined in class Component (see class diagram from -llustration 8.1). The following are the possible mar#up and code for the login page( 9tml(
<html> <head> <title>/ogin page</title> </head> <bod"> <%orm id89login3orm9 method89get9 Cicket:id89login3orm9> <%ieldset> <legend st"le89color: X3SA9>/ogin</legend> <p Cicket:id89login)tatus9></p> <span >0sername: </span><input Cicket:id89username9 t"pe89text9 id89username9 /><br/> <span >#assCord: </span><input Cicket:id89passCord9 t"pe89passCord9 id89passCord9 /> <p> <input t"pe89submit9 name89/ogin9 4alue89/ogin9/> </p> </%ieldset> </%orm> </bod"> </html>
Ka.a code(
public class Dome#age extends Web#age public Dome#age7%inal #age#arameters parameters:
26 Chapter 1E will cover form validation in detail$
64
The e,ample shows how 2ic#et form components can be used to store user input inside their model. 9owe.er we can dramatically impro.e the form code using Compo"ndPropert,Model and its ability to access the properties of its model obHect. The re.isited code is the following (the %ogin;orm"evisited proHect)(
public class /ogin3orm extends 3orm pri4ate )tring username1 pri4ate )tring passCord1 pri4ate )tring login)tatus1 public /ogin3orm7)tring id: super7id:1 set+e%ault;odel7neC Compound#ropert";odel7this::1 add7neC Text3ield79username9::1 add7neC #assCordText3ield79passCord9::1 add7neC /abel79login)tatus9::1 & public %inal 4oid on)ubmit7: i%7username.eEuals79test9: UU passCord.eEuals79test9:: login)tatus 8 9CongratulationsF91 else login)tatus 8 9Wrong username or passCord F91 & &
-n this .ersion the form itself is used as model obHect for its Compo"ndPropert,Model. This allows children components to ha.e direct access to form fields and use them as bac#ing obHects* without e,plicitly creating a model for themsel.es. >ote Meep in mind that when Compo"ndPropert,Model is inherited* it does not consider the ids of tra.ersed containers for the final property e,pression* but it will always use the id of the .isited child. To understand this potential pitfall* letBs consider the following initialiCation code of a page(
//Create a person named ILohn )mithI #erson person 8 neC #erson79Lohn9, 9)mith9:1 //Create a person named ILill )mithI #erson spouse 8 neC #erson79Lill9, 9)mith9:1 //)et Lill as LohnIs spouse person.set)pouse7spouse:1 set+e%ault;odel7neC Compound#ropert";odel7person::1
6$
Web;arkupContainer spouse 8 neC Web;arkupContainer79spouse9:1 /abel name1 spouse.add7name 8 neC /abel79name9::1 add7spouse:1
The .alue displayed by label 9name9 will be 9Lohn9 and not the spouseBs name 9Lill9 as you may e,pect. -n this e,ample the label doesnBt own a model* so it must search up its container hierarchy for an inheritable model. 9owe.er* its container ( WebMark"p Container with id BspouseB) doesnBt own a model* hence the reEuest for a model is forwarded to the parent container* which in this case is the page. -n the end the label inherits Compo"ndPropert,Model from page but only its own id is used for the property e,pression. The containers in between are ne.er ta#en into account for the final property e,pression.
4.#
.omponent 5rop5own.hoice
!lass org.apache.wicket.mark"p.html.0orm.:rop:ownChoice is the form component needed to display a list of possible options as a drop-down list where users can select one of the proposed options. This component must be used with <select> tag( 9tml(
<%orm Cicket:id89%orm9> )elect a %ruit: <select Cicket:id89%ruits9></select> <di4><input t"pe89submit9 4alue89submit9/></di4> </%orm>
Ka.a code(
/ist<)tring> %ruits 8 5rra"s.as/ist79apple9, 9straCberr"9, 9Catermelon9:1 %orm.add7neC +rop+oCnChoice<)tring>79%ruits9, neC ;odel7:, %ruits::1
-n addition to the component id* in order to build a :rop:ownChoice we need to pro.ide to its constructor two further parameters( a model containing the current selected item. This parameter is not reEuired if we are going to inherit a Comp"ndPropert,Model for this component. a list of options to display which can be supplied as a model or as a regular 5a!a."til.List. -n the e,ample abo.e the possible options are pro.ided as a list of String obHects. loo# at the mar#up generated for them( ow letBs ta#e a
63
<select name89%ruits9 Cicket:id89%ruits9> <option 4alue899 selected89selected9>Choose 'ne</option> <option 4alue89A9>apple</option> <option 4alue89@9>straCberr"</option> <option 4alue89=9>Catermelon</option> </select>
The first option is a placeholder item corresponding to a n"ll model .alue. By default :rop:ownChoice cannot ha.e a n"ll .alue so users are forced to select a not-null option. -f we want to change this beha.ior we can set the n"ll-alid flag to tr"e .ia the setN"ll-alid method. Please note that the placeholder te,t (F!hose oneG) can be localiCed* as we will see in chapter 14. The other options are identified by the attribute 4alue. By default the .alue of this attribute is the inde, of the single option inside the pro.ided list of choices* while the te,t displayed to the user is obtained by calling toString( on the choice obHect. This default beha.ior wor#s fine as long as our options are simple obHects li#e strings* but when we mo.e to more comple, obHects we may need to implement a more sophisticated algorithm to generate the .alue to use as the option id and the one to display to user. 2ic#et has sol.ed this problem with org.apache.wicket.mark"p.html.0orm.2Choice)ender interface. This interface defines method get:ispla,-al"e(/ ob5ect that is called to generate the .alue to display for the gi.en choice ob5ect* and method get2d-al"e(/ ob5ect4 int inde( that is called to generate the option id. The built-in implementation of this interface is class org.apache.wicket.mark"p.html.Form. Choice)enderer which renders the two .alues using property e,pressions. -n the following code we want to show a list of Person obHects using their full name as .alue to display and using their passport code as option id( Ka.a code(
/ist<#erson> persons1 //$nitialiPe the list o% persons here... Choice*enderer person*enderer 8 neC Choice*enderer79%ullName9, 9passportCode9:1 %orm.add7neC +rop+oCnChoice<)tring>79persons9, neC ;odel<#erson>7:, persons, person*enderer::1
The choice renderer can be assigned to the :rop:ownChoice using one of its constructor that accepts this type of parameter (li#e we did in the e,ample abo.e) or after its creation in.o#ing setChoice)enderer method.
65
2hat we want to do in this e,ample is to chain the model of the :rop:ownChoice (which contains the selected Person) with the model of the Form. -n this way the Form will wor# with the selected Person as bac#ing obHect. The :rop:ownChoice component can be configured to automatically update its model each time we change the selected item on the client side. All we ha.e to do is to o.erride method want+n SelectionChangedNoti0ications to ma#e it return tr"e. -n practice* when this method returns tr"e* :rop:ownChoice will submit its .alue e.ery time Ka.a"cript e.ent onChange occurs* and its model will be conseEuently updated. To le.erage this functionality* :rop:ownChoice doesnBt need to be inside a form. The following is the resulting mar#up of the e,ample page(
... <bod"> /ist o% persons <select Cicket:id89persons9></select> <br/> <br/> <%orm Cicket:id89%orm9> <di4 st"le89displa": table19> <di4 st"le89displa": table-roC19> <di4 st"le89displa": table-cell19>Name: </di4> <di4 st"le89displa": table-cell19> <input t"pe89text9 Cicket:id89name9/> </di4> </di4> <di4 st"le89displa": table-roC19> <di4 st"le89displa": table-cell19>)urname: </di4> <di4 st"le89displa": table-cell19> <input t"pe89text9 Cicket:id89surname9/> </di4> </di4> <di4 st"le89displa": table-roC19> <di4 st"le89displa": table-cell19>5ddress: </di4> <di4 st"le89displa": table-cell19> <input t"pe89text9 Cicket:id89address9/> </di4> </di4> <di4 st"le89displa": table-roC19> <di4 st"le89displa": table-cell19>(mail: </di4> <di4 st"le89displa": table-cell19> <input t"pe89text9 Cicket:id89email9/> </di4> </di4> </di4> <input t"pe89submit9 4alue89)a4e9/> </%orm> </bod">
6A
...
As choice render we ha.e used the basic implementation pro.ided with the org.apache.wicket .mark"p.html.0orm.Choice)enderer class that we ha.e seen in the pre.ious paragraph. loadPersons( is Hust an utility method which generates a list of Person instances. The model for :rop:ownChoice is a simple instance of the Model class. 9ere is the whole code of the page (e,cept for the loadPersons( method)(
public class #erson/ist+etails extends Web#age pri4ate 3orm %orm1 pri4ate +rop+oCnChoice<#erson> persons/ist1 public #erson/ist+etails7: ;odel<#erson> list;odel 8 neC ;odel<#erson>7:1 Choice*enderer<#erson> person*ender 8 neC Choice*enderer<#erson>79%ullName9:1 persons/ist 8 neC +rop+oCnChoice<#erson>79persons9, list;odel, load#ersons7:, person*ender: !'4erride protected boolean Cant'n)electionChangedNoti%ications7: return true1 & &1 add7persons/ist:1 %orm 8 neC 3orm79%orm9, neC Compound#ropert";odel<#erson>7list;odel::1 %orm.add7neC Text3ield79name9::1 %orm.add7neC Text3ield79surname9::1 %orm.add7neC Text3ield79address9::1 %orm.add7neC Text3ield79email9::1 add7%orm:1 & //load#ersons7: //... &
The two models wor# together as a pipeline where the output of method get+b5ect of Model is the
66
model obHect of Compo"ndPropert,Model. As we ha.e seen* model chaining allows us to combine the actions of two or more models without creating new custom implementations.
This interface pro.ides a method called detach( which is in.o#ed by 2ic#et at the end of web reEuest processing when data model is no more needed but before serialiCation occurs. @.erriding this method we can clean any reference to data obHect #eeping Hust the information needed to retrie.e it later (for e,ample the id of the table row where our data are stored). -n this way we can a.oid the serialiCation of the obHect wrapped into the model o.ercoming both the problem with nonserialiCable obHects and the one with large data obHects. "ince 2Model inherits from 2:etachable* e.ery model of 2ic#et is FdetachableG* although not all of them implement a detaching policy (li#e the Model class). &sually detaching operations are strictly dependent on the persistence technology adopted for model obHects (li#e a relational db* a o"Q: db* a Eueue* etc...)* so itBs not unusual to write a custom detachable model suited for the persistence technology chosen for a gi.en proHect. To ease this tas# 2ic#et pro.ides abstract model Loadable:etachableModel. This class internally holds a transient reference to a model obHect which is initialiCed the first time get+b5ect( is called to precess a reEuest. The concrete data loading is delegated to abstract method / load( . The reference to a model obHect is automatically set to n"ll at the end of the reEuest by the detach( method . The following class diagram summariCes the methods defined inside Loadable:etachableModel.
67
on:etach and onAttach can be o.erridden in order to obtain further control o.er the detaching procedure. ow as e,ample of a possible use of Loadable:etachableModel* we will build a model designed to wor# with entities managed .ia KPA48. To understand the following code a basic #nowledge of KPA is reEuired e.en if we wonBt go into the detail of this standard. Warning The following model is pro.ided for e,ample purposes only and is not intended to be used in production en.ironment. -mportant aspects such as transaction management are not ta#en into account and you should rewor# the code before considering to use it.
public class Lpa/oadable;odel<T> extends /oadable+etachable;odel<T> pri4ate (ntit";anager3actor" entit";anager3actor"1 pri4ate Class<T> entit"Class1 pri4ate )erialiPable identi%ier1 pri4ate /ist<'b2ect> constructor#arams1 public Lpa/oadable;odel7(ntit";anager3actor" entit";anager3actor", T entit": super7:1 #ersistence0nit0til util 8 entit";anager3actor".get#ersistence0nit0til7:1 this.entit";anager3actor" 8 entit";anager3actor"1 this.entit"Class 8 7Class<T>: entit".getClass7:1 this.identi%ier 8 7)erialiPable: util.get$denti%ier7entit":1 set'b2ect7entit":1 & !'4erride protected T load7: T entit" 8 null1 i%7identi%ier F8 null: (ntit";anager entit";anager 8 entit";anager3actor".create(ntit";anager7:1 entit" 8 entit";anager.%ind7entit"Class, identi%ier:1 & return entit"1 &
68
!'4erride protected 4oid on+etach7: super.on+etach7:1 T entit" 8 get'b2ect7:1 #ersistence0nit0til persistence0til 8 entit";anager3actor".get#ersistence0nit0til7:1 i%7entit" 88 null: return1 identi%ier 8 7)erialiPable: persistence0til.get$denti%ier7entit":1 & &
The constructor of the model ta#es as input two parameters( an implementation of the KPA interface 5a!a(.persistence.'ntit,ManagerFactor, to manage KPA entities and the entity that must be handled by this model. -nside its constructor the model sa.es the class of the entity and its id (which could be n"ll if the entity has not been persisted yet). These two informations are reEuired to retrie.e the entity at a later time and are used by the load method. on:etach is responsible for updating the entity id before detachment occurs. The id can change the first time an entity is persisted (KPA generates a new id and assigns it to the entity). Please note that this model is not responsible for sa.ing any changes occurred to the entity obHect before it is detached. -f we donBt want to loose these changes we must e,plicitly persist the entity before the detaching phase occurs. Warning "ince the model of this e,ample holds a reference to the 'ntit,Manager Factor,* the implementation in use must be serialiCable.
7%
2hen we o.erwrite on:etach we must call the super class implementation of this method* usually as last line in our custom implementation.
ThatBs a bad practice and you must a.oid it. &sing models we do not only decouple our components from the data source* but we can also relay on them (if they are dynamic) to wor# with the most up-todate .ersion of our model obHect. -f we decide to bypass models we lose all these ad.antages and we force model obHects to be serialiCed.
4.4 8ummar
<odels are at the core of 2ic#et and they are the basic ingredient needed to taste the real power of the framewor#. -n this chapter we ha.e seen how to use models to bring data to our components without littering their code with technical details about their persistence strategy. 2e ha.e also introduced 2ic#et forms as complementary topic. 2ith forms and models we are able to bring our applications to life allowing them to interact with users. But what we ha.e seen in this chapter about 2ic#et forms is Hust the tip of the iceberg. ThatBs why the ne,t chapter is entirely dedicated to them.
71
74
=or e,ample if we want to use a te,t field to insert an email address* we could use the built-in .alidator 'mailAddress-alidator to ensure that the inserted input will respect the email format local@ partAdomainBC(
Text3ield email 8 neC Text3ield79email9:1 email.add7neC (mail5ddressOalidator7::1
2ic#et comes with a set of built-in .alidators that should suit most of our needs. 2e will see them in paragraph 1%.4.$.
1/.2.1
2ic#et generates a feedbac# message for each field that doesnBt satisfy one of its .alidation rules. =or e,ample the message generated when a reEuired field is left empty is the following
3ield I<label>I is reEuired.
is the .alue of the label model set on a FormComponent with method setLabel(2Model 7String8 model . -f such model is not pro.ided* component id will be used as the default .alue. The entire infrastructure of feedbac# messages is built on top of the Ka.a internationaliCation (-17 ) support and it uses resource bundles=> to store messages.
<label>
>ote The topics of internationaliCation will be co.ered in chapter 14. =or now we will gi.e Hust few notions needed to understand the e,amples from this chapter. By default resource bundles are stored into properties files but we can easily configure other sources as described later in paragraph 14.3.5. Default feedbac# messages (li#e the one abo.e for reEuired fields) are stored in the file $pplication. properties placed inside 2ic#et the org.apache.wicket pac#age. @pening this file we can find the #ey and the localiCed .alue of the message(
*eEuired83ield IW label&I is reEuired.
2e can note the #ey (*eEuired in our case) and the label parameter written in the e0pression language=+ (W label&). "crolling down this file we can also find the message used by the 'mail Address-alidator(
(mail5ddressOalidator8The 4alue o% IW label&I is not a 4alid email address.
By default FormComponent pro.ides $ parameters for feedbac# message( .alidation)* label and name (this later is the id of the component). Warning
input
+emember that component model is updated with the user input only if .alidation succeeds; As a conseEuence* we canBt retrie.e the wrong .alue
30 http: en.wikipedia.org wiki #mail1address 31 http: docs.oracle.com -avase tutorial i>?n resbundle inde0.html 32 http: en.wikipedia.org wiki #0pression1%anguage
7$
inserted for a field from its model. -nstead* we should use get-al"e( method of FormComponent class. (This method will be introduced in the e,ample used in paragraph 1%.4.5)
1/.2.2
To display feedbac# messages we must use component org.apache.wicket.mark"p.html. panel.FeedbackPanel. This component automatically reads all the feedbac# messages generated during form .alidation and displays them with an unordered list(
<ul class89%eedback#anel9> <li class89%eedback#anel(**'*9> <span class89%eedback#anel(**'*9>3ield I0sernameI is reEuired.</span> </li> </ul>
!"" classes Xfeedbac#PanelX and Xfeedbac#PanelI++@+X can be used in order to customiCe the style of the message list$$(
llustration 1E$1% Hxample of styling of feedback messages list The component can be freely placed inside the page and we can set the ma,imum amount of displayed messages with the setMa(Messages( method. Irror messages can be filtered using three built-in filters( .omponentHeed=ack+essageHilter: shows only messages coming from a specific component. .ontainerHeed=ack+essageHilter: shows only messages coming from a specific container or from any of its children components. Drror,evelHeed=ack+essageHilter: shows only messages with a le.el of se.erity eEuals or greater than a gi.en lower bound. !lass FeedbackMessage defines a set of static constants to e,press different le.els of se.erity( DIB&0* I++@+* 2A+ - 0* - =@* "&!!I""* etc.... :e.els of se.erity for feedbac# messages are discussed in paragraph 1%.4.A. These filters are intended to be used when there are more than one feedbac# panel (or more than one form) in the same page. 2e can pass a filter to a feedbac# panel .ia its constructor or using the setFilter method. !ustom filters can be created implementing the 2FeedbackMessageFilter interface. An e,ample of custom filter is illustrated on page 78.
1/.2.3
;uilt?in validators
2ic#et already pro.ides a number of built-in .alidators ready to be used. The following table is a short reference where .alidators are listed along with a brief description of what they do. The default feedbac# message used by each of them is reported as well(
33 The style of llustration 1E$1 was created by 3anko 3ovanovic$ 'ee http: css.d<one.com news css*message*bo0es*different*me
73
>ame
ImailAddressDalidator &rlDalidator
5escription
+essage
!hec#s if input respects the format The 4alue o% IW label&I is not a 4alid email address. local@partAdomain !hec#s if input is a .alid &+:. 2e can The 4alue o% IW label&I is not a specify in the constructor which 4alid 0*/. protocols are allowed (http://* https://* and ftp://). Dalidator class that can be e,tended or used as a factory class to get date .alidators to chec# if a date is bigger than a lower bound (method minim"m(:ate min))* smaller than a upper bound (method ma(im"m(:ate ma( ) or inside a range (method range(:ate min4 :ate ma( ).
Hor minimum validator: The 4alue o% IW label&I is less than the minimum o% W minimum&.
DateDalidator
Hor maximum validator: The 4alue o% the IW label&I maximum o% is W larger than maximum&.
Hor range validator: The 4alue o% IW label&I is not betCeen W minimum& maximum&. and W
+angeDalidator
Dalidator class that can be e,tended or used as a factory class to get .alidators to chec# if a .alue is bigger than a gi.en lower bound (method minim"m(/ min))* smaller than a upper bound (method ma(im"m(/ ma( ) or inside a range (method range(/ min4/ ma( ).
The type of the .alue is a generic Hor range validator: subtype of 5a!a.lang.Comparable The 4alue o% IW label&I must be and must implement Seriali3able betCeen W minimum& and W interface. maximum&. "tringDalidator Dalidator class that can be e,tended or used as a factory class to get .alidators to chec# if the length of a string .alue is bigger then a gi.en lower bound (method minim"mLength (int min )* smaller then a gi.en upper bound (method ma(im"mLength (int ma( ) or within a gi.en range (method length1etween(int min4 int ma( ).
Hor minimum validator: The 4alue o% IW label&I is shorter than the minimum o% W minimum& characters.
Hor maximum validator: The 4alue o% IW label&I is longer than the maximum o% W maximum& characters.
To accept only string .alues consisting Hor range validator: of e,actly n characters* we must use The 4alue o% IW label&I is not method e(actLength(int length . betCeen W minimum& and W
maximum& characters long.
75
Hor exact validator: The 4alue o% IW label&I is not exactl" W exact& characters long.
!redit!ardDalidator
!hec#s if input is a .alid credit card The credit number. This .alidator supports some of in4alid. the most popular credit cards (li#e FAmerican I,pressX* X<aster!ardX* FDisaG or FDiners !lubG).
card
number
is
IEualPassword-nputDalidator This .alidator chec#s if two password W labelA& and W label@& must be eEual. fields ha.e the same .alue.
1/.2.#
-f we donBt li#e the default .alidation feedbac# messages* we can o.erride them pro.iding custom properties files. -n these files we can write our custom messages using the same #eys of the messages we want to o.erride. =or e,ample if we wanted to o.erride the default message for in.alid email addresses* our properties file would contain a line li#e this(
(mail5ddressOalidator8;an, "our email address is not goodF
As we will see in the ne,t chapter* 2ic#et searches for custom properties files in .arious positions inside the applicationBs class path* but for now we will consider Hust the properties file placed ne,t to our application class. The name of this file must be eEual to the name of our application class(
The e,ample proHect 2verride4ail4essage o.errides email .alidatorBs message with a new one which also reports the .alue that failed .alidation(
(mail5ddressOalidator8The 4alue IW input&I inserted %or %ield IW label&I is not a 4alid email address.
1/.2.(
-f our web application reEuires a comple, .alidation logic and built-in .alidators are not enough* we can implement our own custom .alidators. =or e,ample (proHect :sername(ustom6alidator) suppose we are wor#ing on the registration page of our site where users can create their profile choosing their username. @ur registration form should .alidate the new username chec#ing if it was already chosen by
7A
another user. -n a situation li#e this we may need to implement a custom .alidator that Eueries a specific data source to chec# if a username is already in use. =or the sa#e of simplicity* the .alidator of our e,ample will chec# the gi.en username against a fi,ed list of three e,isting usernames. A custom .alidator must simply implement interface 2-alidator(
public class 0sernameOalidator implements $Oalidator<)tring> /ist<)tring> existing0sernames 8 5rra"s.as/ist79bigLack9, 9anon"mous9, 9mr)mith9:1 public 4oid 4alidate7$Oalidatable<)tring> 4alidatable: )tring chosen0serName 8 4alidatable.getOalue7:1 i%7existing0sernames.contains7chosen0serName:: Oalidation(rror error 8 neC Oalidation(rror7this:1 *andom random 8 neC *andom7:1 error.setOariable79suggested0serName9, 4alidatable.getOalue7: > random.next$nt7::1 4alidatable.error7error:1 & & &
The only method defined inside 2-alidator is !alidate(2-alidatable7/8 !alidatable and is in.o#ed during .alidationBs step. -nterface 2-alidatable represents the component being .alidated and it can be used to retrie.e the component model ( getModel( ) or the .alue to .alidate (get-al"e( ). The custom .alidation logic is all inside 2-alidatorBs method !alidate. 2hen .alidation fails a .alidator must use 2-alidatableBs method error(2-alidation'rror error to generate the appropriate feedbac# message. -n the code abo.e we used the -alidation'rror class as con.enience implementation of the 2-alidation'rror interface which represents the .alidation error that must be displayed to the user. This class pro.ides a constructor that uses the class name of the .alidator in input as #ey for the resource to use as feedbac# message (i.e. B 0sernameOalidatorB in the e,ample). -f we want to specify more then one #ey to use to locate the error message* we can use method add;e,(String ke, of -alidation'rror class. -n our e,ample when .alidation fails* we suggest a possible username concatenating the gi.en input with a pseudo-random integer. This .alue is passed to the feedbac# message with a .ariable named s"ggested9serName. The message is inside applicationBs properties file(
0sernameOalidator8The username IW input&I is alread" in use. Tr" Cith IW suggested0serName&I
To pro.ide further .ariables to our feedbac# message we can use method set-ariable(String name4 +b5ect !al"e of class -alidation'rror as we did in our e,ample. The code of the home page of the proHect will be e,amined in the ne,t paragraph after we ha.e introduced the topic of flash messages. 1/.2.0sing flash messages
"o far we ha.e considered Hust the error messages generated during .alidation step. 9owe.er 2ic#etBs Component class pro.ides a set of methods to e,plicitly generate feedbac# messages called flash messages. These methods are(
76
deb"g(Seriali3able message in0o(Seriali3able message s"ccess(Seriali3able message warn(Seriali3able message error(Seriali3able message 0atal(Seriali3able message
Iach of these methods corresponds to a level of severity for the message. The list abo.e is sorted by increasing le.el of se.erity. -n the e,ample seen in the pre.ious paragraph we ha.e a form which uses s"ccess method to notify user when the inserted username is .alid. -nside this form there are two FeedbackPanel components( one to display the error message produced by custom .alidator and the other one to display the success message. The code of the e,ample page is the following( 9tml(
<bod"> <%orm Cicket:id89%orm9> 0sername: <input t"pe89text9 Cicket:id89username9/> <br/> <input t"pe89submit9/> </%orm> <di4 st"le89color:green9 Cicket:id89succes;essage9> </di4> <di4 st"le89color:red9 Cicket:id89%eedback;essage9> </di4> </bod">
Ka.a code(
public class Dome#age extends Web#age public Dome#age7%inal #age#arameters parameters: 3orm %orm 8 neC 3orm79%orm9: !'4erride protected 4oid on)ubmit7: super.on)ubmit7:1 success790sername is goodF9:1 & &1 Text3ield mail1 %orm.add7mail 8 neC Text3ield79username9, ;odel.o%799:::1 mail.add7neC 0sernameOalidator7::1 add7neC 3eedback#anel79%eedback;essage9, neC (xact(rror/e4el3ilter73eedback;essage.(**'*:::1 add7neC 3eedback#anel79succes;essage9, neC (xact(rror/e4el3ilter73eedback;essage.)0CC()):::1 add7%orm:1 & class (xact(rror/e4el3ilter implements $3eedback;essage3ilter
77
pri4ate int error/e4el1 public (xact(rror/e4el3ilter7int error/e4el: this.error/e4el 8 error/e4el1 & public boolean accept73eedback;essage message: return message.get/e4el7: 88 error/e4el1 & & //0sernameOalidator de%inition //... &
The two feedbac# panels must be filtered in order to display Hust the messages with a gi.en le.el of se.erity (I++@+ for .alidator message and "&!!I"" for formBs flash message). &nfortunately the built-in message filter 'rrorLe!elFeedbackMessageFilter is not suitable for this tas# because its filter condition does not chec# for an e,act error le.el (the gi.en le.el is used as lower bound .alue). As a conseEuence* we had to build a custom filter (inner class '(act'rrorLe!elFilter) to accept only the desired se.erity le.el (see method accept of interface 2FeedbackMessageFilter).
-f no type has been pro.ided* FormComponent will try to as# its model for this information. The Propert,Model and Compo"ndPropert,Model models can use reflection to get the type of obHect model. By default* if FormComponent can not obtain the type of its model obHect in any way* it will consider it as a simple String. @nce FormComponent has determined the type of model obHect* it can loo# up for a converter* which is the entity in charge of con.erting input to Ka.a obHect and .ice .ersa. !on.erters are instances of org.apache.wicket."til.con!ert.2Con!erter interface and are registered by our application class on start up. To get a con.erter for a specific type we must call method getCon!erter(Class7C8 t,pe on the interface 2Con!erterLocator returned by ApplicationBs method getCon!erterLocator( (
//retrie4e con4erter %or .oolean t"pe 5pplication.get7:.getCon4erter/ocator7:.getCon4erter7.oolean.class:1
78
>ote !omponents which are subclasses of AbstractSingleSelectChoice donBt follow the schema illustrated abo.e to con.ert user input. These #inds of components (li#e :rop:ownChoice and )adioChoiceBE) use their choice render and their collection of possible choices to perform input con.ersion.
1/.3.1
The default con.erter locator used by 2ic#et is org.apache.wicket.Con!erterLocator. This class pro.ides con.erters for the most common Ka.a types. 9ere we can see the con.erters registered inside its constructor(
public Con4erter/ocator7: set7.oolean.TG#(, .ooleanCon4erter.$N)T5NC(:1 set7.oolean.class, .ooleanCon4erter.$N)T5NC(:1 set7."te.TG#(, ."teCon4erter.$N)T5NC(:1 set7."te.class, ."teCon4erter.$N)T5NC(:1 set7Character.TG#(, CharacterCon4erter.$N)T5NC(:1 set7Character.class, CharacterCon4erter.$N)T5NC(:1 set7+ouble.TG#(, +oubleCon4erter.$N)T5NC(:1 set7+ouble.class, +oubleCon4erter.$N)T5NC(:1 set73loat.TG#(, 3loatCon4erter.$N)T5NC(:1 set73loat.class, 3loatCon4erter.$N)T5NC(:1 set7$nteger.TG#(, $ntegerCon4erter.$N)T5NC(:1 set7$nteger.class, $ntegerCon4erter.$N)T5NC(:1 set7/ong.TG#(, /ongCon4erter.$N)T5NC(:1 set7/ong.class, /ongCon4erter.$N)T5NC(:1 set7)hort.TG#(, )hortCon4erter.$N)T5NC(:1 set7)hort.class, )hortCon4erter.$N)T5NC(:1 set7+ate.class, neC +ateCon4erter7::1 set7Calendar.class, neC CalendarCon4erter7::1 set72a4a.sEl.+ate.class, neC )El+ateCon4erter7::1 set72a4a.sEl.Time.class, neC )ElTimeCon4erter7::1 set72a4a.sEl.Timestamp.class, neC )ElTimestampCon4erter7::1 set7.ig+ecimal.class, neC .ig+ecimalCon4erter7::1 &
-f we want to add more con.erters to our application* we can o.erride ApplicationBs method newCon!erterLocator which is used by application class to build its con.erter locator. To illustrate how to implement custom con.erters and use them in our application* we will build a form with two te,t field( one to input a regular e,pression pattern and another one to input a string .alue that will be split with the gi.en pattern. The first te,t field will ha.e an instance of class 5a!a."til.rege(.Pattern as model obHect. The final page will loo# li#e this (the code of this e,ample is from the (ustom(onverter proHect)(
8%
llustration 1E$*% 7 form with a Text(ield using a #ava$util$regex$Pattern as model ob#ect The con.ersion between Pattern and String is Euite straightforward. The code of our custom con.erter is the following(
public class *eg(xp#atternCon4erter implements $Con4erter<#attern> !'4erride public #attern con4ertTo'b2ect7)tring 4alue, /ocale locale: return #attern.compile74alue:1 & !'4erride public )tring con4ertTo)tring7#attern 4alue, /ocale locale: return 4alue.to)tring7:1 & &
<ethods declared by interface 2Con!erter ta#e as input a Locale parameter in order to deal with locale-sensiti.e data and con.ersions. 2e will learn more about locales and internationaliCation in chapter 14. @nce we ha.e implemented our custom con.erter* we must o.erride method newCon!erterLocator( inside our application class and tell it to add our new con.erter to the default set(
!'4erride protected $Con4erter/ocator neCCon4erter/ocator7: Con4erter/ocator de%ault/ocator 8 neC Con4erter/ocator7:1 de%ault/ocator.set7#attern.class, neC *eg(xp#atternCon4erter7::1 return de%ault/ocator1 &
=inally* in the home page of the proHect we build the form which displays (with a flash message) the to#ens obtained splitting the string with the gi.en pattern(
public class Dome#age extends Web#age pri4ate #attern reg(xp#atter1 pri4ate )tring stringTo)plit1
81
public Dome#age7%inal #age#arameters parameters: Text3ield mail1 Text3ield stringTo)plitTxt1 3orm %orm 8 neC 3orm79%orm9: !'4erride protected 4oid on)ubmit7: super.on)ubmit7:1 )tring message*esult 8 9Tokens %or the gi4en string and pattern:<br/>91 )tringHJ tokens 8 reg(xp#atter.split7stringTo)plit:1 %or 7)tring token : tokens: message*esult >8 9- 9 > token > 9<br/>91 & success7message*esult:1 & &1 %orm.set+e%ault;odel7neC Compound#ropert";odel7this::1 %orm.add7mail 8 neC Text3ield79reg(xp#atter9::1 %orm.add7stringTo)plitTxt 8 neC Text3ield79stringTo)plit9::1 add7neC 3eedback#anel79%eedback;essage9:.set(scape;odel)trings7%alse::1 add7%orm:1 & &
>ote -f the user input can not be con.erted to the target type* FormComponent will generate the default error message F The 4alue o% IW label&I is not a 4alid W t"pe&.G. The bundle #ey for this message is $Con4erter.
At the beginning of this chapter we ha.e seen that form processing is started by process method
84
which ta#es as input an instance of 2FormS"bmitter. This parameter corresponds to the 2FormS"bmittingComponent clic#ed by a user to submit the form and it is n"ll if we ha.e used a standard 9T<: submit button (li#e we ha.e done so far). A submitting component is added to a form Hust li#e any other child component using method add(Component... . A form can ha.e any number of submitting components and we can specify which one among them is the default one by calling the FormBs method set:e0a"lt1"tton(2FormS"bmittingComponent component . The default submitter is the one that will be used when user presses BInterB #ey in a field of the form. -n order to ma#e the default button wor#* 2ic#et will add to our form a hidden <di4> tag containing a te,t field and a submit button with some Ka.a"cript code to trigger it(
<di4 st"le89Cidth:Apx1height:Apx1position:absolute1le%t:-@AApx1top:-@AApx1
o4er%loC:hidden9>
<input t"pe89text9 autocomplete89o%%9/> <input t"pe89submit9 name89submit=9 onclick89 4ar b8document....9/> </di4>
Kust li#e 2ic#et forms* interface 2FormS"bmitter defines methods onS"bmit and on'rror. These two methods ha.e the priority o.er the namesa#e methods of the form* meaning that when a form is submitted with an 2FormS"bmitter* the onS"bmit of the submitter is called before the one of the form. "imilarly* if .alidation errors occurs during the first step of form processing* submitterBs method on'rror is called before the formBs one. >ote "tarting with 2ic#et .ersion A.% interface 2FormS"bmitter defines a further callbac# method called onA0terS"bmit( . This method is called after formBs method onS"bmit( has been e,ecuted.
1/.#.1
!omponent org.apache.wicket.mark"p.html.0orm.1"tton is a basic implementation of a form submitter. -t can be used with either the <input> or <button> tags. The string model recei.ed as input by its constructor is used as button label and it will be the .alue of the mar#up attribute 4alue. -n the following snippet we ha.e a form with two submit buttons bound to an <input> tag. @ne of them is set as default button and both ha.e a string model for the label( 9tml(
<bod"> <%orm Cicket:id89%orm9> 0sername: <input t"pe89text9 Cicket:id89username9/> <br/> <input t"pe89submit9 Cicket:id89submit@9/> <input t"pe89submit9 Cicket:id89submit=9/> </%orm> </bod">
Ka.a code(
public class Dome#age extends Web#age public Dome#age7%inal #age#arameters parameters: 3orm %orm 8 neC 3orm79%orm9:1
8$
%orm.add7neC Text3ield79username9, ;odel.o%799:::1 %orm.add7neC .utton79submit@9, ;odel.o%793irst submitter9:::1 .utton second)ubmitter1 %orm.add7second)ubmitter 8 neC .utton79submit=9, ;odel.o%79)econd submitter9:::1 %orm.set+e%ault.utton7second)ubmitter:1 add7%orm:1 & &
0enerated mar#up(
<%orm Cicket:id89%orm9 id89%orm@9 method89post9 action89?A-@.$3orm)ubmit/istener-%orm9> <di4> ... <F-- Code generated b" Wicket to handle the de%ault button --> ... </di4> 0sername: <input t"pe89text9 Cicket:id89username9 4alue899 name89username9/> <br/> <input t"pe89submit9 Cicket:id89submit@9 name89submit@9 id89submit@R9 4alue893irst submitter9/> <input t"pe89submit9 Cicket:id89submit=9 name89submit=9 id89submit==9 4alue89)econd submitter9/> </%orm>
Another component that can be used to submit a form is org.apache.wicket.mark"p. html.0orm.S"bmitLink. This component uses Ka.a"cript to submit the form. :i#e the name suggests* the component can be used with the <a> tag but it can be also bound to any other tag that supports the e.ent handler onclick. 2hen used with the <a> tag* the Ka.a"cript code needed to submit the form will be placed inside hre% attribute while with other tags the script will go inside the e.ent handler onclick. A notable difference between this component and 1"tton is that S"bmitLink can be placed outside the form it must submit. -n this case we must specify the form to submit in its constructor( 9tml(
<html xmlns:Cicket89http://Cicket.apache.org9> <head> </head> <bod"> <%orm Cicket:id89%orm9> #assCord: <input t"pe89passCord9 Cicket:id89passCord9/> <br/> </%orm> <button Cicket:id89external)ubmitter9> )ubmit </button> </bod"> </html>
Ka.a code(
83
public class Dome#age extends Web#age public Dome#age7%inal #age#arameters parameters: 3orm %orm 8 neC 3orm79%orm9:1 %orm.add7neC #assCordText3ield79passCord9, ;odel.o%799:::1 //speci%" the %orm to submit add7neC )ubmit/ink79external)ubmitter9, %orm::1 add7%orm:1 & &
1/.#.2
2ith an 2FormS"bmittingComponent we can choose to s#ip the default form submission process by setting the appropriate flag to false with the set:e0a"ltFormProcessing method. 2hen the default form processing is disabled only the submitterBs onS"bmit is called while formBs .alidation and models updating are s#ipped. This can be useful if we want to implement a F!ancelG button on our form which redirects user to another page without .alidating his/her input. 2hen we set this flag to false we can decide to manually in.o#e the form processing by calling the process(2FormS"bmittingComponent method.
2hen a form is submitted also its nested forms are submitted and they participate in the .alidation step. This means that if a nested form contains in.alid input .alues* the outer form wonBt be submitted. @n the contrary* nested forms can be singularly submitted without depending on the status of their outer form. To submit a parent form when one of its children forms is submitted* we must o.erride its method wantS"bmit+nNestedFormS"bmit and ma#e it return tr"e.
35 'ee http: www.w=.org 4ark:p html= forms.html where it is stated% :There can be several forms in a single document6 but the ("A/ element can:t be nested$:
85
Ka.a code(
%orm.add7neC Text5rea79description9, ;odel.o%799:::1
!omponent /e(tArea is used Hust li#e any other single-line te,t field. To specify the siCe of the te,t area we can write attributes roCs and cols directly in the mar#up file or we can create new attribute modifiers and add them to our /e(tArea component.
9tml(
<html> <head> </head> <bod"> <h@>0pload "our %ile hereF</h@> <%orm Cicket:id89%orm9> <input t"pe89%ile9 Cicket:id89%ile0pload3ield9/> <input t"pe89submit9 4alue890pload9/> </%orm> <di4 Cicket:id89%eedback#anel9> </di4> </bod"> </html>
Ka.a code(
public class Dome#age extends Web#age pri4ate 3ile0pload3ield %ile0pload3ield1 public Dome#age7%inal #age#arameters parameters: %ile0pload3ield 8 neC 3ile0pload3ield79%ile0pload3ield9:1 3orm %orm 8 neC 3orm79%orm9: !'4erride protected 4oid on)ubmit7: super.on)ubmit7:1 3ile0pload %ile0pload 8 %ile0pload3ield.get3ile0pload7:1
8A
tr" 3ile %ile 8 neC 3ile7)"stem.get#ropert"792a4a.io.tmpdir9: > 9/9 > %ile0pload.getClient3ileName7::1 %ile0pload.CriteTo7%ile:1 & catch 7$'(xception e: e.print)tackTrace7:1 & & &1 %orm.set;ulti#art7true:1 //set a limit %or uploaded %ileIs siPe %orm.set;ax)iPe7."tes.kilob"tes7@AA::1 %orm.add7%ile0pload3ield:1 add7neC 3eedback#anel79%eedback#anel9::1 add7%orm:1 & &
The code that copies the uploaded file to the temporary directory is inside the onS"bmit method of the Form class. The uploaded file is handled with an instance of class File9pload returned by the getFile9pload( method of the File9ploadField class. This class pro.ides a set of methods to perform some common tas#s li#e getting the name of the uploaded file ( getClientFileName( )* coping the file into a directory ( write/o(destinationFile )* calculating file digest (get:igest (digestAlgorithm ) and so on. Form component can limit the siCe for uploaded files using its setMa(Si3e(si3e method. -n the e,ample we ha.e set this limit to 1%% #b to pre.ent users from uploading files bigger than this siCe. >ote The ma,imum siCe for uploaded files can also be set at applicationBs le.el using the set:e0a"ltMa(im"m9ploadSi3e(1,tes ma(Si3e method of the 2ApplicationSettings interface(
!'4erride public 4oid init7: get5pplication)ettings7:.set+e%ault;aximum0pload)iPe7."tes.
kilob"tes7@AA::1
&
1/.'.1
-f we need to upload multiple files at once* we can use the M"ltiFile9ploadField component which allows the user to select an arbitrary number of files to send on form submission. An e,ample showing how to use this component can be found in 2ic#et module wicket@e(amples in file M"lti9ploadPage.5a!a. The li.e e,ample is hosted at http(//www.wic#et-library.com/wic#ete,amples-A.%.,/upload/multi.
86
mar#up and with an arbitrary number of children components. 2hile itBs perfectly legal to use Panel also to group form components* the resulting component wonBt be itself a form component and it wonBt participate in the formBs submission wor#flow. This could be a strong limitation if the custom component needs to coordinate its children during subtas#s li#e input con.ersion or model updating. ThatBs why in 2ic#et we ha.e the org.apache. wicket.mark"p.html.0orm.FormComponentPanel component which combines the features of a Panel (it has its own mar#up file) and a FormComponent (it is a subclass of FormComponent). A typical scenario in which we may need to implement a custom FormComponentPanel is when our web application and its users wor# with different units of measurement for the same data. To illustrate this possible scenario* letBs consider a form where a user can insert a temperature that will be recorded after being con.erted to Mel.in degrees (see the e,ample proHect (ustom;orm (omponentPanel). The Mel.in scale is wildly adopted among the scientific community and it is one of the se.en base units of the -nternational "ystem of &nits $A* so it ma#es perfect sense to store temperatures e,pressed with this unit of measurement. 9owe.er* in our e.eryday life we still use other temperature scales li#e !elsius or =ahrenheit* so it would be nice to ha.e a component which internally wor#s with Mel.in degrees and automatically applies con.ersion between Mel.in temperature scale and the one adopted by the user. -n order to implement such a component* we can ma#e a subclass of FormComponentPanel and le.erage the con!ert2np"t and on1e0ore)ender methods( in the implementation of the con!ert2np"t method we will con.ert input .alue to Mel.in degrees while in the implementation of on1e0ore)ender method we will ta#e care of con.erting the Mel.in .alue to the temperature scale adopted by the user. @ur custom component will contain two children components( a te,t field to let user insert and edit a temperature .alue and a label to display the letter corresponding to userBs temperature scale (= for =ahrenheit and ! for !elsius). The resulting mar#up file is the following(
<html> <head> </head> <bod"> <Cicket:panel> *egistered temperature: <input siPe89R9 maxlength89R9 Cicket:id89registeredTemperature9/> <label Cicket:id89mesurament0nit9></label> </Cicket:panel> </bod"> </html>
As shown in the mar#up abo.e FormComponentPanel uses the same <Cicket:panel> tag used by Panel to define its mar#up. ow letBs see the Ka.a code of the new form component starting with the on2nitiali3e( method(
public class Temperature+egree3ield extends 3ormComponent#anel<+ouble> pri4ate Text3ield<+ouble> user+egree1 public Temperature+egree3ield7)tring id: super7id:1
36 http%CCen$wikipedia$orgCwikiC nternationalD'ystemDofD@nits
87
& public Temperature+egree3ield7)tring id, $;odel<+ouble> model: super7id, model:1 & !'4erride protected 4oid on$nitialiPe7: super.on$nitialiPe7:1 5bstract*ead'nl";odel<)tring> label;odel8neC 5bstract*ead'nl";odel<)tring>7: !'4erride public )tring get'b2ect7: i%7get/ocale7:.eEuals7/ocale.0):: return 9Y391 return 9YC91 & &1 add7neC /abel79mesurament0nit9, label;odel::1 add7user+egree8neC Text3ield<+ouble>79registeredTemperature9, neC ;odel<+ouble>7:::1 user+egree.setT"pe7+ouble.class:1 &
-nside the on2nitiali3e method we ha.e created a read-only model for the label that displays the letter corresponding to the userBs temperature scale. To determinate which temperature scale is in use* we retrie.e the Locale from the session by calling ComponentBs getLocale( method (we will tal# more about this method in chapter 14). Then* if locale is the one corresponding to the &nited "tates* the chosen scale will be =ahrenheit* otherwise it will be considered as !elsius. -n the final part of on2nitiali3e( we add the two components to our custom form component. 'ou may ha.e noticed that we ha.e e,plicitly set the type of model obHect for the te,t field to double. This is necessary as the starting model obHect is a n"ll reference and this pre.ents the component from automatically determining the type of its model obHect. ow we can loo# at the rest of the code containing the con!ert2np"t and on1e0ore)ender methods(
!'4erride protected 4oid con4ert$nput7: +ouble user+egreeOal 8 user+egree.getCon4erted$nput7:1 +ouble kel4in+egree1 i%7get/ocale7:.eEuals7/ocale.0):: kel4in+egree 8 user+egreeOal > <TS.Z[1 .ig+ecimal bd6el4in 8 neC .ig+ecimal7kel4in+egree:1 .ig+ecimal %raction 8 neC .ig+ecimal7T:.di4ide7neC .ig+ecimal7S::1 kel4in+egree 8 bd6el4in.multipl"7%raction:.doubleOalue7:1 &else kel4in+egree 8 user+egreeOal > =[R.@T1 &
88
setCon4erted$nput7kel4in+egree:1 & !'4erride protected 4oid on.e%ore*ender7: super.on.e%ore*ender7:1 +ouble kel4in+egree 8 7+ouble: get+e%ault;odel'b2ect7:1 +ouble user+egreeOal 8 null1 i%7kel4in+egree 88 null: return1 i%7get/ocale7:.eEuals7/ocale.0):: .ig+ecimal bd6el4in 8 neC .ig+ecimal7kel4in+egree:1 .ig+ecimal %raction 8 neC .ig+ecimal7S:.di4ide7neC .ig+ecimal7T::1 kel4in+egree 8 bd6el4in.multipl"7%raction:.doubleOalue7:1 user+egreeOal 8 kel4in+egree - <TS.Z[1 &else user+egreeOal 8 kel4in+egree - =[R.@T1 & user+egree.set;odel'b2ect7user+egreeOal:1 & &
"ince our component does not directly recei.e the user input* con!ert2np"t( must read this .alue from the inner te,t field using FormComponentBs getCon!erted2np"t( method which returns the input .alue already con.erted to the type specified for the component ( :o"ble in our case). @nce we ha.e the user input we con.ert it to #el.in degrees and we use the resulting .alue to set the con.erted input for our custom component (using method setCon!erted2np"t(/ con!erted2np"t ). <ethod on1e0ore)ender( is responsible for synchroniCing the model of the inner te,tfield with the model of our custom component. To do this we retrie.e the model obHect of the custom component with the get:e0a"ltModel+b5ect( method* then we con.ert it to the temperature scale adopted by the user and finally we use this .alue to set the model obHect of the te,t field.
1%%
To a.oid these #inds of problems we should build a stateless login page which does not depend on a user session. 2ic#et pro.ides a special .ersion of the Form component called StatelessForm which is stateless by default (i.e its method getStatelessHint( returns true)* hence itBs an ideal solution when we want to build a stateless page with a form. A possible implementation of our login form is the following (e,ample proHect Stateless%ogin;orm)( 9tml(
<html> <head> <meta charset89ut%-B9 /> </head> <bod"> <di4>)ession is <b Cicket:id89sessionT"pe9></b></di4> <br/> <di4>T"pe IuserI as correct credentials</di4> <%orm Cicket:id89%orm9> <%ieldset> 0sername: <input t"pe89text9 Cicket:id89username9/> <br/> #assCord: <input t"pe89passCord9 Cicket:id89passCord9/><br/> <input t"pe89submit9/> </%ieldset> </%orm> <br/> <di4 Cicket:id89%eedback#anel9></di4> </bod"> </html>
Ka.a code(
public class Dome#age extends Web#age pri4ate /abel sessionT"pe1 pri4ate )tring passCord1 pri4ate )tring username1 public Dome#age7%inal #age#arameters parameters: )tateless3orm %orm 8 neC )tateless3orm79%orm9: !'4erride protected 4oid on)ubmit7: //sign in i% username and passCord are QuserN i%79user9.eEuals7username: UU username.eEuals7passCord:: in%o790sername and passCord are correctF9:1 else error79Wrong username or passCord9:1 & &1 %orm.add7neC #assCordText3ield79passCord9::1 %orm.add7neC Text3ield79username9::1 add7%orm.set+e%ault;odel7neC Compound#ropert";odel7this:::1 add7sessionT"pe 8 neC /abel79sessionT"pe9, ;odel.o%799:::1
1%1
add7neC 3eedback#anel79%eedback#anel9::1 & !'4erride protected 4oid on.e%ore*ender7: super.on.e%ore*ender7:1 i%7get)ession7:.isTemporar"7:: sessionT"pe.set+e%ault;odel'b2ect79temporar"9:1 else sessionT"pe.set+e%ault;odel'b2ect79permanent9:1 & &
:abel sessionT"pe shows if current session is temporary or not and is set inside on1e0ore)ender( ( if our page is really stateless the session will be always temporary. 2e ha.e also inserted a feedbac# panel in the home page that shows if the credentials are correct. This was done to ma#e the e,ample form more interacti.e.
A chec# bo, can be used as single component to set a boolean property. =or this purpose 2ic#et pro.ides the org.apache.wicket.mark"p.html.0orm.Check1o( component which must be attached to <input t"pe89checkbox9.../> tag. -n the ne,t e,ample (proHect Single(heck9o0) we will consider a form similar to the one used in paragraph 8.5 to edit a Person obHect* but with an additional chec#bo, to let the user decide if she wants to subscribe to our mailing list or not. The form uses the following bean as bac#ing obHect(
public class *egistration$n%o implements )erialiPable pri4ate )tring name1 pri4ate )tring surname1 pri4ate )tring address1 pri4ate )tring email1 pri4ate boolean subscribe/ist1 /*-etters and setters*/ &
The mar#up and the code for this e,ample are the following( =ormBs mar#up(
Wicket free user guide
1%4
<%orm Cicket:id89%orm9> <di4 st"le89displa": table19> <di4 st"le89displa": table-roC19> <di4 st"le89displa": table-cell19>Name: </di4> <di4 st"le89displa": table-cell19> <input t"pe89text9 Cicket:id89name9/> </di4> </di4> <di4 st"le89displa": table-roC19> <di4 st"le89displa": table-cell19>)urname: </di4> <di4 st"le89displa": table-cell19> <input t"pe89text9 Cicket:id89surname9/> </di4> </di4> <di4 st"le89displa": table-roC19> <di4 st"le89displa": table-cell19>5ddress: </di4> <di4 st"le89displa": table-cell19> <input t"pe89text9 Cicket:id89address9/> </di4> </di4> <di4 st"le89displa": table-roC19> <di4 st"le89displa": table-cell19>(mail: </di4> <di4 st"le89displa": table-cell19> <input t"pe89text9 Cicket:id89email9/> </di4> </di4> <di4 st"le89displa": table-roC19> <di4 st"le89displa": table-cell19>)ubscribe list:</di4> <di4 st"le89displa": table-cell19> <input t"pe89checkbox9 Cicket:id89subscribe/ist9/> </di4> </di4> </di4> <input t"pe89submit9 4alue89)a4e9/> </%orm>
Page constructor(
public Dome#age7%inal #age#arameters parameters: *egistration$n%o registrtion$n%o 8 neC *egistration$n%o7:1 registrtion$n%o.set)ubscribe/ist7true:1 3orm %orm 8 neC 3orm79%orm9, neC Compound#ropert";odel<*egistration$n%o>7registrtion$n%o::1 %orm.add7neC Text3ield79name9::1 %orm.add7neC Text3ield79surname9::1 %orm.add7neC Text3ield79address9::1 %orm.add7neC Text3ield79email9::1 %orm.add7neC Check.ox79subscribe/ist9::1 add7%orm:1
1%$
&
Please note that the chec#bo, will be initially selected because we ha.e set to true the subscribe flag during the model obHect creation (with instruction registrtion$n%o.set)ubscribe/ist7tr"e:)(
1/.1/.1
2hen we need to display a gi.en number of options with chec#bo,es* we can use the org.apache. wicket.mark"p.html.0orm.Check1o(M"ltipleChoice component. =or e,ample* -f our options are a list of strings* we can display them in this way( 9tml(
<di4 Cicket:id89check-roup9> <input t"pe89checkbox9/>$t Cill be replaced b" the actual checkboxes... </di4>
Ka.a code(
/ist<)tring> %ruits 8 5rra"s.as/ist79apple9, 9straCberr"9, 9Catermelon9:1 %orm.add7neC Check.ox;ultipleChoice79check-roup9, neC /ist;odel<)tring>7neC 5rra"/ist<)tring>7::, %ruits::1
This component can be attached to a <di4> tag or to a <span> tag. o specific content is reEuired for this tag as it will be populated with the actual chec#bo,es. "ince this component allows multiple selection* its model obHect is a list. -n the e,ample abo.e we ha.e used model class org.apache. wicket.model."til.ListModel which is specifically designed to wrap a List obHect. By default Check1o(M"ltipleChoice inserts a <br/> tag as suffi, after each option. 2e can configure both the suffi, and the prefi, used by the component with the setPre0i( and setS"00i( methods. 2hen our options are more comple, obHects than simple strings* we can render them using an 2Choice)ender* as we did for :rop:ownChoice in paragraph 8.3( 9tml(
<di4 Cicket:id89check-roup9>
1%3
Ka.a code(
#erson 2ohn 8 neC #erson79Lohn9, 9)mith9:1 #erson bob 8 neC #erson79.ob9, 9)mith9:1 #erson 2ill 8 neC #erson79Lill9, 9)mith9:1 /ist<#erson> the)miths 8 5rra"s.as/ist72ohn, bob, 2ill:1 Choice*enderer render 8 neC Choice*enderer79name9:1 %orm.add7neC Check.ox;ultipleChoice79check-roup9, neC /ist;odel<)tring>75rra"/ist<)tring>7::, the)miths, render::1
1/.1/.2
A nice feature we can offer to users when we ha.e a group of chec#bo,es is a FspecialG chec#bo, which selects/unselects all the other options of the group(
2ic#et comes with a couple of utility components that ma#e it easy to implement such a feature. They are Checkbo(M"ltipleChoiceSelector and Check1o(Selector classes* both inside org. apache.wicket.mark"p.html.0orm pac#age. The difference between these two components is that the first wor#s with an instance of Check1o(M"ltipleChoice while the second ta#es in input a list of Check1o( obHects( !hec#bo,<ultiple!hoice"elector usage(
Check.ox;ultipleChoice check-roup1 //check-roup initialiPation... Checkbox;ultipleChoice)elector cbmcs 8 neC Checkbox;ultipleChoice)elector79id9, check-roup:1
!hec#Bo,"elector usage(
Check.ox check.ox@, check.ox=, check.oxR1 //checks initialiPation... Check.ox)elector cbmcs 8 neC Check.ox)elector79id9, check.ox@, check.ox=, check.oxR:1
1/.1/.3
=or groups of radio buttons we can use the org.apache.wicket.mark"p.html.0orm. )adioChoice component which wor#s in much the same way as Check1o(M"ltipleChoice(
1%5
9tml(
<di4 Cicket:id89radio-roup9> <input t"pe89radio9/>$t Cill be replaced b" actual radio buttons... </di4>
Ka.a code(
/ist<)tring> %ruits 8 5rra"s.as/ist79apple9, 9straCberr"9, 9Catermelon9:1 %orm.add7neC *adioChoice79radio-roup9, ;odel.o%799:, %ruits::1
Kust li#e Check1o(M"ltipleChoice* this component pro.ides the setPre0i( and setS"00i( methods to configure the prefi, and suffi, for our options and it supports 2Choice)ender as well. -n addition* )adioChoice pro.ides the want+nSelectionChangedNoti0ications( method to notify the web ser.er when the selected option changes (this is the same method seen for :rop:ownChoice in paragraph 8.3).
ow the user can select multiple options by holding down !trl #ey (or !ommand #ey for <ac) and selecting them. To wor# with multiple choice list 2ic#et pro.ides the org.apache.wicket.mark"p.html.0orm. ListM"ltipleChoice component( 9tml(
<select Cicket:id89%ruits9> <option>choice @</option> <option>choice =</option> </select>
Ka.a code(
/ist<)tring> %ruits 8 5rra"s.as/ist79apple9, 9straCberr"9, 9Catermelon9:1 %orm.add7neC /ist;ultipleChoice79%ruits9, neC /ist;odel<)tring>7neC 5rra"/ist<)tring>7::,
1%A
%ruits::1
This component must be bound to a <select> tag but the attribute multiple89multiple9 is not reEuired as it will automatically be added by the component. The number of .isible rows can be set with the setMa()ows(int ma()ows method.
1/.11.1
.omponent Palette
2hile multiple choice list sol.es the problem of handling a big number of multiple choices* it is not much intuiti.e for end users. ThatBs why des#top 0&-s ha.e introduced a more comple, component which can be generally referred to as multi select transfer component (it doesnBt ha.e an actual official name)(
llustration 1E$1% 7n example of multi select transfer component from )asper iAeport This #ind of component is composed by two multiple-choice lists* one on the left displaying the a.ailable options and the other one on the right displaying the selected options. &ser can mo.e options from a list to another by double clic#ing on them or using the buttons placed between the two list. Built-in org.apache.wicket.e(tensions.mark"p.html.0orm.palette.Palette component pro.ides an out-of-the-bo, implementation of a multi select transfer component. -t wor#s in a similar way to ListM"ltipleChoice( 9tml(
<di4 Cicket:id89palette9>
1%6
)elect Cill be replaced b" the actual content... <select multiple89multiple9> <option>option@</option> <option>option=</option> <option>optionR</option> </di4>
Ka.a code(
#erson 2ohn 8 neC #erson79Lohn9, 9)mith9:1 #erson bob 8 neC #erson79.ob9, 9)mith9:1 #erson 2ill 8 neC #erson79Lill9, 9)mith9:1 #erson andrea 8 neC #erson795ndrea9, 9)mith9:1 /ist<#erson> the)miths 8 5rra"s.as/ist72ohn, bob, 2ill, andrea:1 Choice*enderer render 8 neC Choice*enderer79name9:1 %orm.add7neC #alette79palette9, ;odel.o%7neC 5rra"/ist<)tring>7::, neC /ist;odel<)tring> 7the)miths:, render, T, true::1
The last two parameters of the PaletteBs constructor (an integer .alue and a boolean .alue) are* respecti.ely* the number of .isible rows for the two lists and a flag to choose if we want to display the two optional buttons which mo.e selected options up and down. The descriptions of the two lists (FA.ailableG and F"electedG) can be customiCed pro.iding two resources with #eys palette.a4ailable and palette.selected. The mar#up of this component uses a number of !"" classes which can be e,tended/o.erriden to customiCe the style of the component. 2e can find these classes and see which tags they decorate in the default mar#up file of the component(
<table cellspacing89A9 cellpadding89=9 class89palette9> <tr> <td class89header header54ailable9><span Cicket:id89a4ailableDeader9>Ha4ailable headerJ</span></td> <td>UX@ZA1</td> <td class89header header)elected9><span Cicket:id89selectedDeader9>Hselected headerJ</span> </td> </tr> <tr> <td class89pane choices9> <select Cicket:id89choices9 class89choices)elect9>HchoicesJ</select>
1%7
</td> <td class89buttons9> <button t"pe89button9 Cicket:id89add.utton9 class89button add9><di4/> </button><br/> <button t"pe89button9 Cicket:id89remo4e.utton9 class89button remo4e9><di4/> </button><br/> <button t"pe89button9 Cicket:id89mo4e0p.utton9 class89button up9><di4/> </button><br/> <button t"pe89button9 Cicket:id89mo4e+oCn.utton9 class89button doCn9><di4/> </button><br/> </td> <td class89pane selection9> <select class89selection)elect9 Cicket:id89selection9>HselectionJ</select> </td> </tr> </table>
1/.12 8ummar
=orms are the standard solution to let users interact with our web applications. -n this chapter we ha.e seen the three steps in.ol.ed with the form processing wor#flow in 2ic#et. 2e ha.e started loo#ing at form .alidation and feedbac# messages generation* then we ha.e seen how 2ic#et con.erts input .alues into Ka.a obHects and .ice .ersa. -n the second part of the chapter we learnt how to build reusable form components and how to implement a stateless form. 2e ha.e ended the chapter with an o.er.iew of the built-in form components needed to handle standard input form elements li#e chec#bo,es* radio buttons and multiple selections lists.
1%8
To ease this tas# 2ic#et pro.ides a number of special-purpose components called repeaters which are designed to use their related mar#up to display the items of a gi.en set in a more natural and less chaotic way. -n this chapter we will see some of the built-in repeaters that come with 2ic#et.
Ka.a code(
*epeatingOieC list$tems 8 neC *epeatingOieC79list$tems9:1 list$tems.add7neC /abel7list$tems.neCChild$d7:, 9green9:1 list$tems.add7neC /abel7list$tems.neCChild$d7:, 9blue9:1 list$tems.add7neC /abel7list$tems.neCChild$d7:, 9red9:1
11%
0enerated mar#up(
<ul> <li>green</li> <li>blue</li> <li>red</li> </ul>
As we can see in this e,ample* each child component has been rendered using the parent mar#up as if it was its own.
111
-n this e,ample we ha.e displayed the full name of two PersonBs instances. The most interesting part of the code is the implementation of method pop"late2tem where parameter item is the current child component created by List-iew and its model contains the corresponding element of the list. Please note that inside pop"late2tem we must add nested components to item obHect and not directly to the List-iew.
11.2.1
By default List-iew replaces its children components with new instances e.ery time is rendered. &nfortunately this beha.ior is a problem if List-iew is inside a form and it contains form components. The problem is caused by the fact that children components are replaced by new ones before form is rendered* hence they canBt #eep their input .alue if .alidation fails and* furthermore* their feedbac# messages can not be displayed. To a.oid this #ind of problem we can force List-iew to reuse its children components using its method set)e"se2tems and passing tr"e as parameter. -f for any reason we need to refresh children components after we ha.e in.o#ed set)e"se2tems(tr"e * we can use Mark"pContainer#s method remo!eAll( to force List-iew to rebuild them.
114
persons.add7;odel.o%7neC #erson79Lohn9, 9)mith9::1 persons.add7;odel.o%7neC #erson79+an9, 9Wong9::1 add7neC *e%reshingOieC<#erson>79persons9: !'4erride protected 4oid populate$tem7$tem<#erson> item: item.add7neC /abel79%ullName9, neC #ropert";odel7item.get;odel7:, 9%ullName9:::1 & !'4erride protected $terator<$;odel<#erson>> get$tem;odels7: return persons.iterator7:1 & &:1 &
11.3.1
By default* Hust li#e List-iew* )e0reshing-iew replaces its children with new instances e.ery time is rendered. The strategy that decides if and how children components must be refreshed is returned by method get2tem)e"seStrateg,. This strategy is an implementation of interface 22tem)e"seStrateg,. The default implementation used by )e0reshing-iew is class :e0a"lt2tem)e"seStrateg, but 2ic#et pro.ides also strategy )e"se20Models'6"alStrateg, which reuses an item if its model has been returned by the iterator obtained with method get2temModels. To set a custom strategy we must use method set2tem)e"seStrateg,.
11.#.1
.omponent 5ataEiew
!lass org.apache.wicket.mark"p.repeater.data.:ata-iew is the simplest pageable repeater shipped with 2ic#et. :ata-iew comes with abstract method pop"late2tem(2tem that must be implemented to configure children components. -n the following e,ample we use a :ata-iew to display a list of Person obHects in a 9T<: table(
Wicket free user guide
11$
9tml(
<table> <tr> <th>Name</th><th>)urename</th><th>5ddress</th><th>(mail</th> </tr> <tr Cicket:id89roCs9> <td Cicket:id89data*oC9></td> </tr> </table>
Ka.a code(
//method load#ersons is de%ined elseChere /ist<#erson> persons 8 load#ersons7:1 /ist+ata#ro4ider<#erson> list+ata#ro4ider 8 neC /ist+ata#ro4ider<#erson>7persons:1 +ataOieC<#erson> dataOieC 8 neC +ataOieC<#erson>79roC9, list+ata#ro4ider: !'4erride protected 4oid populate$tem7$tem<#erson> item: #erson person 8 item.get;odel'b2ect7:1 *epeatingOieC repeatingOieC 8 neC *epeatingOieC79data*oC9:1 repeatingOieC.add7neC /abel7repeatingOieC.neCChild$d7:, person.getName7:::1 repeatingOieC.add7neC /abel7repeatingOieC.neCChild$d7:, person.get)urename7:::1 repeatingOieC.add7neC /abel7repeatingOieC.neCChild$d7:, person.get5ddress7:::1 repeatingOieC.add7neC /abel7repeatingOieC.neCChild$d7:, person.get(mail7:::1 item.add7repeatingOieC:1 & &1 add7dataOieC:1
Please note that in the code abo.e we ha.e used also a )epeating-iew component to populate the rows of the table. -n the ne,t paragraph we will see a similar e,ample that adds support for data paging.
11.#.2
5ata paging
To enable data paging on a pageable repeaters* we must first set the number of items to display per page with method set2temsPerPage(long items . Then* we must attach the repeater to panel PagingNa!igator (placed in pac#age org.apache.wicket.mark"p.html.na!igation .paging) which is responsible for rendering a na.igation bar containing the lin#s illustrated in the following picture(
ProHect Page@ata6iew#0ample mi,es a :ata-iew component with a PagingNa!igator to display the list of all countries of the world sorted by alphabetical order $6. 9ere is the initialiCation code of the proHect home page(
37 The list of countries is read from a csv file downloaded from http: opengeocode.org download cow.php
113
9tml(
<table> <tr> <th>$)' R@ZZ-@</th><th>Name</th><th>/ong name</th><th>Capital</th><th>#opulation</th> </tr> <tr Cicket:id89roCs9> <td Cicket:id89data*oC9></td> </tr> </table>
Ka.a code(
public Dome#age7%inal #age#arameters parameters: super7parameters:1 //method loadCountries3romCs4 is de%ined elseChere in the class. //$t reads countries data %rom a cs4 %ile and returns each roC as an arra" o% )trings. /ist<)tringHJ> countries 8 loadCountries3romCs47:1 /ist+ata#ro4ider<)tringHJ> list+ata#ro4ider 8 neC /ist+ata#ro4ider<)tringHJ>7countries:1 +ataOieC<)tringHJ> dataOieC 8 neC +ataOieC<)tringHJ>79roCs9, list+ata#ro4ider: !'4erride protected 4oid populate$tem7$tem<)tringHJ> item: )tringHJ countries5rr 8 item.get;odel'b2ect7:1 *epeatingOieC repeatingOieC 8 neC *epeatingOieC79data*oC9:1 %or 7int i 8 A1 i < countries5rr.length1 i>>: repeatingOieC.add7neC /abel7repeatingOieC.neCChild$d7:, countries5rrHiJ::1 & item.add7repeatingOieC:1 & &1 dataOieC.set$tems#er#age7@T:1 add7dataOieC:1 add7neC #agingNa4igator79pagingNa4igator9, dataOieC::1 &
The data of a single country (-"@ code* name* long name* capital and population) are handled with an array of strings. The usage of PagingNa!igator itBs Euite straightforward as we need to simply pass the pageable repeater to its constructor. To e,plore the other pageable repeaters shipped with 2ic#et you can .isit the page at http(//www.wic#et-library.com/wic#et-e,amples/repeater/ where you can find li.e e,amples of these components. >ote 2ic#et pro.ides also component PageableList-iew which is a sublcass of List-iew that implements interface 2Pageable* hence it can be considered a pageable repeaters e.en if it doesnBt use interface 2:ataPro!ider as data source.
11.( 8ummar
115
-n this chapter we ha.e e,plored the built-in set of components called repeaters which are designed to repeat their own mar#up in output to display a set of items. 2e ha.e started with component )epeating-iew which can be used to repeat a simple mar#up fragment. Then* we ha.e seen components List-iew and )e0reshing-iew which should be used when the mar#up to repeat contains nested components to populate. =inally* we ha.e discussed those repeaters that support data paging and that are called pageable repeaters. 2e ended the chapter loo#ing at an e,ample where a pageable repeater is used with panel PagingNa!igator to ma#e its dataset na.igable by the user.
11A
-n chapter 1% we ha.e seen how the topic of localiCation is in.ol.ed in the generation of feedbac# messages and we had a first contact with resource bundles. -n this chapter we will continue to e,plore the localiCation support pro.ided by 2ic#et and we will learn how to build pages and components ready to be localiCed in different languages.
12.1 ,ocali6ation
As we ha.e seen in chapter 1%* the infrastructure of feedbac# messages is built on top of Ka.a internationaliCation (i17n) support* so it should not be surprising that the same infrastructure is used also for localiCation purpose. 9owe.er* while so far we ha.e used only the A$pplication(lass)ameB.properties file to store our custom messages* in this chapter we will see that also pages* components* .alidators and e.en Ka.a pac#ages can ha.e their own resource bundles. This allows us to split bundles into multiple files #eeping them close to where they are used. But before di.ing into the details of internationaliCation with 2ic#et* itBs worthwhile to Euic#ly re.iew how i17n wor#s under Ka.a* see what classes are in.ol.ed and how they are integrated into 2ic#et. >ote Pro.iding a full description of Ka.a support for i17n is clearly out of the scope of this document. -f you need more informations about this topic you can find them in the Ka.aDocs and in the official i17n tutorial $7.
=or e,ample a bundle with M,1"ndle as base name and localiCed for <andarin !hinese (language code 3h* country code CH* .ariant cmn) will ha.e M,1"ndleG3hGCHGcmn as full name.
38 http: docs.oracle.com -avase tutorial i>?n inde0.html
116
A base name can be a fully Eualified class name* meaning that it can include a pac#age name before the actual base name. The specified pac#age will be the container of the gi.en bundle. =or e,ample if we use org.0oo.M,1"ndle as base name* the bundle named M,1"ndle will be searched inside pac#age org.0oo. The actual base name (M,1"ndle in our e,ample) will be used to build the full name of the bundle following the same rules seen abo.e. )eso"rce1"ndle is an abstract factory class* hence it e,poses a number of factory methods named get1"ndle to load a concrete bundle. 2ithout going into too much details we can say that a bundle corresponds to a file in the classpath. To find a file for a gi.en bundle* get1"ndle needs first to generate an ordered list of candidate bundle names. These names are the set of all possible full names for a gi.en bundle. =or e,ample if we ha.e org.0oo.M,1"ndle as base name and the current locale is the one seen before for <andarin !hinese* the candidate names will be( 1. org.0oo.M,1"ndleG3hGCHGcmn 2. org.0oo.M,1"ndleG3hGCH 3. org.0oo.M,1"ndleG3h 4. org.0oo.M,1"ndle The list of these candidate names is generated starting from the most specific one and subtracting an optional parameter at each step. The last name of the list corresponds to the default resource bundle which is the most general name and is eEual to the base name. @nce that get1"ndle has generated the list of candidate names* it will iterate o.er them to find the first one for which is possible to load a class or a properties file. The class must be a subclass of )eso"rce1"ndle ha.ing as class name the full name used in the current iteration. -f such a class is not found* get1"ndle will try to locate a properties file ha.ing a file name eEuals to the current full name (Ka.a will automatically append e,tension .properties to the full name). =or e,ample gi.en the resource bundle of the pre.ious e,ample* Ka.a will search first for class org.0oo.M,1"ndleG3hGCHGcmn and then for file M,1"ndleG3hGCHGcmn.properties inside pac#age org.0oo. -f no file is found for any of the candidate names* a Missing)eso"rce'(ception will be thrown. Bundles contains local-dependent string resources identified by a #ey that is uniEue in the gi.en bundle. "o once we ha.e obtained a .alid bundle we can access these obHects with method getString (String ke, . As we ha.e seen before wor#ing with feedbac# messages* in 2ic#et most of the times we will wor# with properties files rather than with bundle classes. -n chapter 1% we used a properties file ha.ing as base name the class name of the application class and without any information about the locale. This file is the default resource bundle for a 2ic#et application. -n paragraph 14.3 we will e,plore the algorithm used in 2ic#et to locate the a.ailable bundles for a gi.en component. @nce we ha.e learnt how to le.erage this algorithm* we will be able to split our bundles into more files organiCed in a logical hierarchy.
117
12.3.1
-n addition to localeBs informations* 2ic#et supports two further parameters to identify a resource bundle( style and variation. Parameter style is a string .alue and is defined at session-le.el. To set/get the style for the current session we can use the corresponding setter and getter of class Session(
)ession.get7:.set)t"le79m")t"le9:1 )ession.get7:.get)t"le7:1
-f set* styleBs .alue contributes to the final full name of the bundle and it is placed between the base name and the localeBs informations(
<base name>HMst"leJHM<language code>HM<C'0NT*GMC'+(>HM<4ariant code>JJJ
2ic#et gi.es the priority to candidate names containing the style information (if a.ailable). The other parameter we can use for localiCation is variation. Kust li#e style also variation is a string .alue* but it is defined at component-le.el. The .alue of .ariation is returned by ComponentBs method get-ariation( . By default this method returns the .ariation of the parent component or a n"ll .alue if a component hasnBt a parent (i.e. itBs a page). -f we want to customiCe this parameter we must o.erwrite method get-ariation and ma#e it return the desired .alue. DariationBs .alue contributes to the final full name of the bundle and is placed before style parameter(
<base name>HM4ariationJHMst"leJHM<language code>HM<C'0NT*GMC'+(>HM<4ariant code>JJJ
12.3.2
Ka.a uses the standard character set -"@ 7758-1 $8 to encode te,t files li#e properties files. &nfortunately -"@ 7758-1 does not support most of the e,tra-Iuropean languages li#e !hinese or Kapanese. The only way to use properties files with such languages is to use escaped &nicode characters3%* but this leads to not human-readable files. =or e,ample if we wanted to write the word BwebsiteB in simplified !hinese (the ideograms are ) we should write the &nicode characters \u[3T@\u[5+S. ThatBs why starting from .ersion 1.5* Ka.a introduced the support for L<: files as resource bundles. L<: files are generally encoded with character sets &T=-7 or &T=-1A which support e.ery symbol of the &nicode standard. -n order to be a .alid resource bundle the L<: file must conform to the DTD a.ailable at http(//Ha.a.sun.com/dtd/properties.dtd. 9ere is an e,ample of L<: resource bundle ta#en from proHect %ocali<edCreetings (file WicketApplicationG3h.properties.(ml) containing the translation in simplified !hinese of the greeting message F2elcome to the website;G(
<?xml 4ersion89@.A9 encoding890T3-B9?> <F+'CTG#( properties )G)T(; 9http://2a4a.sun.com/dtd/properties.dtd9> <properties> <entr" ke"89greeting;essage9></entr"> </properties>
To use L<: bundles in 2ic#et we donBt need to put in place any additional configuration. The only rule we ha.e to respect with these files is to use properties.(ml as e,tension while their base name
39 http: en.wikipedia.org wiki IS2 I#(1??DE*> 40 http: en.wikipedia.org wiki %ist1of1:nicode1characters
118
12.3.3
!lass Component ma#es reading bundles .ery easy with method getString(String ke, . This method searches for a resource with the gi.en #ey loo#ing into the resource bundles .isited by the loo#up algorithm illustrated in paragraph 14.3. =or e,ample if we ha.e a greeting message with #ey greetingMessage in our applicationBs resource bundle* we can read it from our component code with this instruction(
get)tring79greeting;essage9:1
12.3.#
-n chapter 1% we ha.e used as resource bundle the properties file placed ne,t to our application class. This file is the default resource bundle for the entire application and it is used by the loo#up algorithm if it doesnBt find any better match for a gi.en component and locale. -f we want to pro.ide localiCed .ersions of this file we must simply follow the rules of Ka.a i17n and put our translated resources into another properties file with a name corresponding to the desired locale. =or e,ample proHect %ocali<edCreetings comes with the default applicationBs properties file ( WicketApplication.properties) containing a greeting message(
greeting;essage8Welcome to the siteF
Along with this file we can also find a bundle for 0erman ( WicketApplicationGde.properties) and another one in L<: format for simplified !hinese (WicketApplicationG3h.properties.(ml). The e,ample proHect consists of a single page ( HomePage.5a!a) displaying the greeting message. The current locale can be changed with a drop-down list and the possible options are Inglish (the default one)* 0erman and simplified !hinese(
llustration 1*$1% The home page of pro#ect 0ocali<edGreetings locali<ed in German The label displaying the greeting message has a custom read-only model which returns the message with method getString. The initialiCation code for this label is this(
5bstract*ead'nl";odel<)tring> model 8 neC 5bstract*ead'nl";odel<)tring>7: !'4erride public )tring get'b2ect7: return get)tring79greeting;essage9:1 & &1 add7neC /abel79greeting;essage9, model::1
14%
!lass org.apache.wicket.model.Abstract)ead+nl,Model is a con.enience class for implementing read-only models. -n this proHect we ha.e implemented a custom read-only model for illustrati.e purposes only because 2ic#et already pro.ides built-in models for the same tas#. 2e will see them in paragraph 14.A. The rest of the code of the home page builds the stateless form and the drop-down menu used to change the locale.
/ist</ocale> locales 8 5rra"s.as/ist7/ocale.(N-/$)D, /ocale.CD$N()(, /ocale.-(*;5N:1 %inal +rop+oCnChoice</ocale> change/ocale 8 neC +rop+oCnChoice</ocale>79change/ocale9, neC ;odel</ocale>7:, locales:1 )tateless3orm %orm 8 neC )tateless3orm79%orm9: !'4erride protected 4oid on)ubmit7: )ession.get7:.set/ocale7change/ocale.get;odel'b2ect7::1 & &1 set)tatelessDint7true:1 add7%orm.add7change/ocale::
12.3.(
Although resource bundles e,ist to e,tract local-dependent elements from our code and from &components* in 2ic#et we can decide to pro.ide different mar#up files for different locale settings. Kust li#e standard mar#up files* by default localiCed mar#up files must be placed ne,t to componentBs class and their file name must contain the localeBs informations. -n the following picture* C"stomPanel comes with a standard (or default) mar#up file and with another one localiCed for 0erman(
llustration 1*$*% Component with markup locali<ed in German 2hen the current locale corresponds to 0erman country (language code de)* mar#up file !ustomPanelOde.html will be used in place of the default one.
12.3.-
"tring resources can be also retrie.ed directly from mar#up code using tag of the desired resource is specified with attribute ke"(
The #ey
can be adopted also to localiCe the attributes of a tag. The name of the attribute and the resource #ey are e,pressed as a colon-separated .alue. -n the following mar#up the content of attribute 4alue will be replaced with the localiCed resource ha.ing Bke"<4alueB as #ey(
Cicket:message
Wicket free user guide
141
-f we want to specify multiple attributes at once* we can separate them with a coma(
<input t"pe89submit9 4alue89#re4ieC 4alue9 Cicket:message894alue:ke"<4alue,title:ke"<title9/>
12.#.1
"imilarly to application class* also component classes can ha.e their own bundle files ha.ing as base name the class name of the related component and placed in the same pac#age. "o for e,ample if class C"stomPanel is a custom panel we created* we can pro.ide it with a default bundle file called C"stomPanel.properties containing the te,tual resources used by this panel. This rule applies to page classes as well(
llustration 1*$1% Page and panel with their own bundle @ne fundamental thing to #eep in mind when we wor# with these #inds of bundles is that the loo#up algorithm gi.es priority to the bundles of the containers of the component that is reEuesting a localiCed resource. The more a container is higher in the hierarchy* the bigger is its priority o.er the other components. This mechanism was made to allow containers to o.erwrite resources used by children components. As a conseEuence the .alues inside the resource bundle of a page will ha.e the priority o.er the other .alues with the same #ey defined in the bundles of children components. To better grasp this concept letBs consider the component hierarchy depicted in the following picture(
144
-f C"stomPanel tries to retrie.e the string resource ha.ing B messageB as #ey* it will get the .alue and not the one defined inside its own bundle file. The default message-loo#up algorithm is not limited to component hierarchy but it also includes the class hierarchy of e.ery component .isited in the search strategy described so far. This ma#es bundle files inheritable* Hust li#e mar#up files. 2hen the hierarchy of a container component is e,plored* any ancestor has the priority o.er children components. !onsider for e,ample the hierarchy in the following picture(
IWellcomeFI
llustration 1*$9% The bundle of CustomPanel is overwritten by the one of page class BasePage$
"imilarly to the pre.ious e,ample* the bundle owned by C"stomPanel is o.erwritten by the bundle of page class 1asePage (which has been inherited by C"stomPage).
12.#.2
.omponent?specific resources
-n order to ma#e a resource specific for a gi.en child component* we can prefi, the message #ey with the id of the desired component. !onsider for e,ample the following code and bundle of a generic page( Page code(
add7neC /abel79label9,neC *esource;odel79labelOalue9:::1 add7neC /abel79another/abel9,neC *esource;odel79labelOalue9:::1
Page bundle(
labelOalue8+e%ault 4alue another/abel.labelOalue8Oalue %or another/abel
:abel with id another/abel will display the .alue IOalue %or another/abelI while label label will display -n a similar fashion* parent containers can specify a resource for a nested child component prepending also its relati.e path (the path is dot-separated)(
I+e%ault 4alueI.
Page code(
Wicket free user guide
14$
Page bundle(
labelOalue8+e%ault 4alue another/abel.labelOalue8Oalue %or another/abel %orm.another/abel.labelOalue8Oalue %or another/abel inside %orm
2ith the code and the bundle abo.e* the label inside the form will display the .alue
another/abel inside %ormI.
IOalue %or
12.#.3
Package =undles
-f no one of the pre.ious steps can find a resource for the gi.en #ey* the algorithm will loo# for package bundles. These bundles ha.e package as base name and they can be placed in one of the pac#age of our application(
llustration 1*$8% Package bundles in each package of the application Pac#ages are tra.ersed starting from the one containing the component reEuesting for a resource and going up to the root pac#age.
12.#.#
The algorithm described so far applies to feedbac# messages as well. -n case of .alidation errors* the component that has caused the error will be considered as the component which the string resource is relati.e to. =urthermore* Hust li#e application class and components* .alidators can ha.e their own bundles placed ne,t to their class and ha.ing as base name their class name. This allows us to distribute .alidators along with the messages they use to report errors(
143
DalidatorBs resource bundles ha.e the lowest priority in the loo#up algorithm. They can be o.erwritten by resource bundles of components* pac#ages and application class.
12.#.(
2ic#et implements the default loo#up algorithm using the strategy patternF>. The concrete strategies are abstracted with the interface org.apache.wicket.reso"rce.loader.2String)eso"rce Loader. By default 2ic#et uses the following implementations of 2String)eso"rceLoader (sorted by e,ecution order)( 1. .omponent8tring3esource,oader( implements most of the default algorithm. -t searches for a gi.en resource across bundles from the container hierarchy* from class hierarchy and from the gi.en component. 4. Package8tring3esource,oader( searches into pac#age bundles. $. .lass8tring3esource,oader( searches into bundles of a gi.en class. By default the target class is the application class. 3. Ealidator8tring3esource,oader( searches for resources into .alidatorBs bundles. A list of .alidators is pro.ided by the form component that failed .alidation. 5. !nitiali6er8tring3esource,oader( this resource allows internationaliCation to interact with the initialiCation mechanism of the framewor# that will be illustrated in paragraph 15.3. De.eloper can customiCe loo#up algorithm remo.ing default resource loaders or adding custom implementations to the list of the resource loaders in use. This tas# can be accomplished using method getString)eso"rceLoaders of setting interface org.apache.wicket.settings. 2)eso"rceSettings(
!'4erride public 4oid init7: super.init7:1 //retrie4e $*esource)ettings and then the list o% resource loaders /ist<$)tring*esource/oader> resource/oaders8 get*esource)ettings7:. get)tring*esource/oaders7:1 //customiPe the list...
145
/ist</ocale> locales 8 5rra"s.as/ist7/ocale.(N-/$)D, /ocale.$T5/$5N, /ocale.-(*;5N:1 /ist<)tring> colors 8 5rra"s.as/ist79green9, 9red9, 9blue9, 9"elloC9:1 %inal +rop+oCnChoice</ocale> change/ocale 8 neC +rop+oCnChoice</ocale>79change/ocale9, neC ;odel</ocale>7:, locales:1 )tateless3orm %orm 8 neC )tateless3orm79%orm9: !'4erride protected 4oid on)ubmit7: )ession.get7:.set/ocale7change/ocale.get;odel'b2ect7::1 & &1 +rop+oCnChoice<)tring> selectColor 8 neC +rop+oCnChoice<)tring>79selectColor9, neC ;odel<)tring>7:, colors: !'4erride protected boolean localiPe+ispla"Oalues7: return true1 & &1 %orm.add7selectColor:1 add7%orm.add7change/ocale::1 &
0erman bundle(
selectColor.null8Wahlen sie eine %arbe green8-run red8*ot blue8.lau "elloC8-elb
-talian bundle(
selectColor.null8)cegli un colore green8Oerde red8*osso blue8.lu "elloC8-iallo
Along with the localiCed .ersions of colors names* in the bundles abo.e we can also find a custom .alue for the placeholder te,t (F!hose one G) used for n"ll .alue. The resource #ey for this resource is InullI or I<component id>.nullI if we want to ma#e it component-specific.
14A
-nternationaliCation is another good chance to taste the power of models. 2ic#et pro.ides two built-in models to better integrate our components with string resources( they are )eso"rceModel and String)eso"rceModel.
12.-.1
3esource+odel
<odel org.apache.wicket.model.)eso"rceModel acts Hust li#e the read-only model we ha.e implemented in paragraph 14.$.3. -t simply retrie.es a string resource corresponding to a gi.en #ey(
//build a *esource;odel %or ke" Igreeting;essageI neC *esource;odel79greeting;essage9:1
2e can also specify a default .alue to use if the reEuested resource is not found(
//build a *esource;odel Cith a de%ault 4alue neC *esource;odel79not(xisting*esource9, 9*esource not %ound.9:1
12.-.2
8tring3esource+odel
<odel org.apache.wicket.model.String)eso"rceModel allows to wor# with comple, and dynamic string resources containing parameters and property e,pressions. The basic constructor of this model ta#es in input a resource #ey and another model. This further model can be used by both the #ey and the related resource to specify dynamic .alues with property e,pressions. =or e,ample letBs say that we are wor#ing on an e-commerce site which has a page where users can see an o.er.iew of their orders. To handle the state of userBs orders we will use the following bean and enum (the code is from proHect String"esource4odel#0ample)( Bean(
public class 'rder implements )erialiPable pri4ate +ate order+ate1 pri4ate '*+(*M)T5T0) status1 public 'rder7+ate order+ate, '*+(*M)T5T0) status: super7:1 this.order+ate 8 order+ate1 this.status 8 status1 & //-etters and setters %or pri4ate %ields &
Inum(
public enum '*+(*M)T5T0)
ow what we want to do in this page is to print a simple label which displays the status of an order and the date on which the order has been submitted. All the informations about the order will be passed to a
Wicket free user guide
146
String)eso"rceModel with a model containing the bean +rder. The bundle in use contains the following #ey/.alue pairs(
order)tatus.A8Gour pa"ment submitted on W order+ate& has been accepted. order)tatus.@8Gour order submitted on W order+ate& is in progress. order)tatus.=8Gour order submitted on W order+ate& has been shipped. order)tatus.R8Gour order submitted on W order+ate& has been deli4ered.
The .alues abo.e contain a property e,pression ( W order+ate&) that will be e.aluated on the data obHect of the model. The same techniEue can be applied to the resource #ey in order to load the right resource according to the state of the order(
'rder order 8 neC 'rder7neC +ate7:, '*+(*M)T5T0).$NM#*'-*()):1 add7neC /abel79order)tatus9, neC )tring*esource;odel79order)tatus.W status.code&9, ;odel.o%7order::::1
As we can see in the code abo.e also the #ey contains a property e,pression ( W status.code&) which ma#es its .alue dynamic. -n this way the state of an obHect (an 'rder in our e,ample) can determinate which resource will be loaded by String)eso"rceModel. -f we donBt use properties e,pressions we can pro.ide a n"ll .alue as model and in this case String)eso"rceModel will beha.e e,actly as a )eso"rceModel. String)eso"rceModel supports also the same parameter substitution used by standard class 5a!a.te(t.MessageFormat. Parameters can be generic obHects but if we use a model as parameter* String)eso"rceModel will use the data obHect inside it as actual .alue (it will call get+b5ect on the model). Parameters are passed to constructor as a .ararg argument. 9ere is an e,ample of usage of parameter substitution( Ka.a code(
#ropert";odel propert";odel 8 neC #ropert";odel<'rder>7order, 9order+ate9:1 //build a string model Cith tCo parameters: a propert" model and an integer 4alue )tring*esource;odel srm 8 neC )tring*esource;odel79order)tatus.dela"9, null, propert";odel, R:1
Bundle(
order)tatus.dela"8Gour order submitted on W A& has been dela"ed b" @& da"s.
@ne further parameter we can specify when we build a String)eso"rceModel is the component that must be used by the loo#up algorithm. ormally this parameter is not rele.ant* but if we need to use a particular bundle owned by a component not considered by the algorithm* we can specify this component as second parameter. -f we pass all possible parameters to String)eso"rceModelBs constructor we obtain something li#e this(
neC )tring*esource;odel79m"6e"9, m"Component, m";odel, param@, param=, paramR,...:1
12.' 8ummar
-nternationaliCation is a mandatory step if we want to ta#e our applications (and our business;) abroad. !hoosing the right strategy to manage our localiCed resources is fundamental to a.oid to ma#e a mess
147
of them. -n this chapter we ha.e e,plored the built-in support for localiCation pro.ided by 2ic#et* and we ha.e learnt which solutions it offers to manage resource bundles. -n the final part of the chapter we ha.e seen how to localiCe the options displayed by a component (such as :rop:ownChoice or )adioChoice) and we also introduced two new models specifically designed to localiCe our components without introducing in their code any detail about internationaliCation.
148
1$%
llustration 11$1% 7 component with two package resources 4a C'' and a )ava'cript file5$
2ith pac#age resources custom components become independent and self*contained and client code can use them without worrying about their dependencies. To load pac#age resources 2ic#et pro.ides class org.apache.wicket.re6"est.reso"rce. Package)eso"rce)e0erence. To identify a pac#age resource we need to specify a class inside the target pac#age and the name of the desired resource (most of the times this will be a file name). -n the following e,ample ta#en from proHect Image$sPackage"es* C"stomPanel loads a picture file a.ailable as pac#age resource and it displays it in a <img> tag using the built-in component org. apache.wicket.mark"p.html.image.2mage( 9tml(
<html> <head>...</head> <bod"> <Cicket:panel> #ackage resource image: <img Cicket:id89package*es#icture9/> </Cicket:panel> </bod"> </html>
Ka.a code(
public class Custom#anel extends #anel public Custom#anel7)tring id: super7id:1 #ackage*esource*e%erence resource*e%erence 8 neC #ackage*esource*e%erence7getClass7:, 9calendar.2pg9:1 add7neC $mage79package*es#icture9, resource*e%erence::1 & &
2ic#et will ta#e care of generating a .alid &+: for file the following structure(
calendar.2pg.
<path to application root>/Cicket/resource/<%ull" Euali%ied class name>/<resource %ile name> -<4er-<id>>H.<%ile extension>J
-n our e,ample the &+: for our picture file calendar.2pg is the following(
1$1
./Cicket/resource/org.CicketTutorial.Custom#anel/calendar-4er-@=S[BB[T<=AAA.2pg
The first part of the &+: is the relati.e path to the application root. -n our e,ample our page is already at the applicationBs root so we ha.e only a single-dotted segment. The ne,t two segments* Cicket and resource* are respecti.ely the namespace and the identifier for resources seen in paragraph 7.A.3. The fourth segment is the fully Eualified name of the class used to locate the resource and it is the scope of the pac#age resource. -n the last segment of the &+: we can find the name of the resource (the file name). As you can see 2ic#et has automatically appended to the file name a .ersion identifier ( 4er@=S[BB[T<=AAA). 2hen 2ic#et runs in DIDI:@P<I T mode this identifier contains the timestamp in millisecond indicating the last time the resource file was modified. This can be useful when we are de.eloping our application and resource files are freEuently modified. Appending the timestamp to the original name we are sure that our browser will use always the last .ersion of the file and not an old* out of date* cached .ersion. 2hen instead 2ic#et is running in DIP:@'<I T mode* the .ersion identifier will contain the <D5 digest of the file instead of the timestamp. The digest is computed only the first time the resource is reEuested. This perfectly ma#es sense as static resources donBt change so often when our application runs into production en.ironment and when this appends the application is redeployed. >ote Pac#age resources can be localiCed following the same rules seen for resource bundles and mar#up files(
llustration 11$*% Package resource locali<ed for (rench language -n the e,ample illustrated in the picture abo.e* if we try to retrie.e pac#age resource calendar.2pg when the current locale is set to =rench* the actual file returned will be calendarM%r.2pg.
13.3.1
-n paragraph 7.$ we ha.e used tag <Cicket:link> to automatically create lin#s to boo#mar#able pages. The same techniEue can be used also for pac#age resources in order to use them directly from mar#up file. :etBs assume for e,ample that we ha.e a picture file called icon.png placed in the same pac#age of the current page. &nder these conditions we can display the picture file using the following mar#up fragment(
<Cicket:link> <img src89icon.png9/> </Cicket:link>
src
resource files.
icon.png. <Cicket:link>
<link>
<script>
for Ka.a"cript
1$$
Please note that in the code abo.e we ha.e built a resource reference using a &+: to the desired library instead of a pac#age resource holding the physical file. The same method get:ependencies( is defined also for class Header2tem.
1$3
resource*esponse.setWriteCallback7neC WriteCallback7: !'4erride public 4oid Crite+ata75ttributes attributes: throCs $'(xception 'utput)tream output)tream 8 attributes.get*esponse7:.get'utput)tream7:1 Writer Criter 8 neC 'utput)treamWriter7output)tream:1 )"nd3eed'utput output 8 neC )"nd3eed'utput7:1 tr" output.output7get3eed7:, Criter:1 & catch 73eed(xception e: throC neC Wicket*untime(xception79#roblems Criting %eed to response...9:1 & & &:1 return resource*esponse1 & // method get3eed7:... &
<ethod new)eso"rce)esponse returns an instance of )eso"rce)esponse representing the response generated by the custom resource. "ince +"" feeds are based on L<:* in the code abo.e we ha.e set the type of the response to text/xml and the te,t encoding to ut%-B. To specify the content that will be returned by our resource we must also pro.ide an implementation of inner class WriteCallback which is responsible for writing content data to responseBs output stream. -n our proHect we used class S,ndFeed+"tp"t from +ome framewor# to write our feed to response. <ethod getFeed( is Hust an utility method that generates a sample +"" feed (which is an instance of interface com.s"n.s,ndication.0eed.s,nd.S,ndFeed). ow that we ha.e our custom resource in place* we can use it in the home page of the proHect. The easiest way to ma#e a resource a.ailable to users is to e,pose it with lin# component )eso"rceLink(
add7neC *esource/ink79rss/ink9, neC *))#roducer*esource7:::1
-n the ne,t paragraphs we will see how to register a resource at application-le.el and how to mount it to an arbitrary &+:.
1$5
2ith the configuration abo.e (ta#en from proHect (ustom"esource4ounting) e.ery reEuest to will be ser.ed by the custom resource built in the pre.ious paragraph. Parameter placeholders are supported as well(
!'4erride public 4oid init7: super.init7:1 //resource mounted to path /%oo Cith a reEuired indexed parameter *esource*e%erence resource*e%erence 8 neC *esource*e%erence79rss#roducer9: *))*eader*esource rss*esource 8 neC *))*eader*esource7:1 !'4erride public $*esource get*esource7: return rss*esource1 &&1 mount*esource79/bar/W baP&9, resource*e%erence:1 &
/%oo/bar
ow to use an application-shared resource we can simply retrie.e it using class Shared)eso"rce )e0erence and pro.iding the #ey pre.iously used to register the resource(
add7neC *esource/ink79global*ss/ink9, neC )hared*esource*e%erence79global*))#roducer9:::1
The &+: generated for application shared resources follows the same pattern seen for pac#age resources(
./Cicket/resource/org.apache.Cicket.5pplication/global*))#roducer
The last segment of the &+: is the #ey of the resource while the pre.ious segment contains the scope of the resource. =or application-scoped resources the scope is always the fully Eualified name of class Application. This should not be surprising since global resources are .isible at application le.el (i.e.
Wicket free user guide
1$A
the scope is the application). >ote Pac#age resources are also application-shared resources but they donBt need to be e,plicitly registered. >ote +emember that we can get the &+: of a resource reference using method "rlFor()eso"rce)e0erence reso"rce)e04 PageParameters params a.ailable with both class )e6"estC,cle and class Component.
The default locator used by 2ic#et is class )eso"rceStreamLocator which in turn tries to load a reEuested resource using a set of implementations of interface 2)eso"rceFinder. This interface defines method 0ind(Class class4 String pathname which tries to resol.e a resource corresponding to the gi.en class and path. The default implementation of 2)eso"rceFinder used by 2ic#et is ClassPath)eso"rceFinder which searches for resources into the application class path. This is the implementation we ha.e used so far in our e,amples. 9owe.er some de.elopers may prefer storing mar#up files and other resources in a separate folder rather than placing them side by side with Ka.a classes. To customiCe resource loading we can add further resource finders to our application in order to e,tend the resource-loo#up algorithm to different locations. 2ic#et already comes with two other implementations of 2)eso"rceFinder designed to search for resources into a specific folder on the file system. The first is class Path and itBs defined in pac#age org.apache.wicket."til.0ile. The constructor of this class ta#es in input an arbitrary folder that can be e,pressed as a string path or as an instance of 2ic#et utility class Folder (in pac#age org.apache.wicket."til.0ile). The second implementation of interface 2)eso"rceFinder is class WebApplicationPath which loo#s into a folder placed inside webappBs root path (but not inside folder 2IB-- =). ProHect (ustom;olderF4arkup#0ample uses WebApplicationPath to load the mar#up file and the resource bundle for its home page from a custom folder. The folder is called markup3older and it is placed in the root path of the webapp. The following picture illustrates the file structure of the proHect(
1$6
As we can see in the picture abo.e* we must preser.e the pac#age structure also in the custom folder used as resource container. The code used inside application class to configure WebApplicationPath is the following(
!'4erride public 4oid init7: get*esource)ettings7:.get*esource3inders7:.add7 neC Web5pplication#ath7get)er4letContext7:, 9markup3older9::1 &
<ethod get)eso"rceFinders( defined by setting interface 2)eso"rceSettings returns the list of resource finders defined in our application. The constructor of WebApplicationPath ta#es in input also an instance of standard interface 5a!a(.ser!let.Ser!letConte(t which can be retrie.ed with WebApplicationBs method getSer!letConte(t( . >ote By default* if resource files can not be found inside application classpath* 2ic#et will search for them inside FresourcesG folder. 'ou may ha.e noted this folder in the pre.ious picture. -t is placed ne,t to the folder FHa.aG containing our source files(
1$7
This folder can be used to store resource files without writing any configuration code.
13.1/ 8ummar
-n this chapter we ha.e learnt how to manage resources with the built-in mechanism pro.ided by 2ic#et. 2ith this mechanism we handle resources from Ka.a code and 2ic#et will automatically ta#e care of generating a .alid &+: for them. 2e ha.e also seen how resources can be bundled as package resources with a component that depends on them to ma#e it self*contained. Then* in the second part of the chapter* we ha.e built a custom resource and we ha.e learnt how to mount it to an arbitrary &+: and how to ma#e it globally a.ailable as shared resource. =inally* in the last part of the paragraph we too# a pee# at the mechanism pro.ided by the framewor# to customiCe the locations where the resource-loo#up algorithm searches for resources.
1$8
llustration 13$1% How our custom datepicker will look like 4locali<ed for Hnglish5$ Warning @n -nternet you can find different libraries that already offer a strong integration between 2ic#et and KQuery3A. The goal of this chapter is to see how to integrate 2ic#et with a Ka.a"cript framewor# building a simple homemade datepic#er which is not intended to pro.ide e.ery feature of the original Ka.a"cript widget.
1#.1.1
43 44 45 46
http: -5uery.com http: -5ueryui.com 'ee component org$apache$wicket$datetime$markup$html$form$.ateText(ield 'ee #&wicket 4http: code.google.com p -5wicket 5 6 wi&uery 4http: code.google.com p wi5uery 5 or Wicket F )Juery @ 4http: www.Gthweb. net wicketF#&ueryFuiC5
13%
Before starting to write code* we must clearly define what features we want to implement for our component. The new component should( ;e self?contained( we must be able to distribute it and use it in other proHects without reEuiring any #ind of additional configuration. Have a customi6a=le date format( de.eloper must be able to decide the date format used to display date .alue and to parse user input. ;e locali6a=le( the pop-up calendar must be localiCable in order to support different languages. ThatBs what weBd li#e to ha.e with our custom datepic#er. -n the rest of the chapter we will see how to implement the features listed abo.e and which resources must be pac#aged with our component.
1#.2.1
KQuery &- needs the following static resources in order to wor# properly( <1uer ?ui.min.<s( the minified .ersion of the library. <1uer ?ui.css( the !"" containing the style used by KQuery &- widgets. <1uer ?ui?i12n.min.<s( the minified Ka.a"cript containing the built-in support for localiCation. Holder AimagesA( the folder containing picture files used by KQuery &- widgets. -n the following picture we can see these pac#age resources with our component class (named .H"er,:ateField)(
Along with the four static resources listed abo.e* we can find also file calendar.Hpg* which is the calendar icon used to open the pop up calendar* and file KQDatePic#er.Hs which contains the following custom Ka.a"cript code that binds our component to a KQuery &- datepic#er(
%unction initLV+atepicker7input$d, countr"$soCode, date3ormat, calendar$con:
131
4ar localiPed5rra" 8 W.datepicker.regionalHcountr"$soCodeJ1 localiPed5rra"HIbutton$mageIJ 8 calendar$con1 localiPed5rra"HIdate3ormatIJ 8 date3ormat1 initCalendar7localiPed5rra":1 W79X9 > input$d:.datepicker7localiPed5rra":1 &1 %unction initCalendar7localiPed5rra": localiPed5rra"HIchange;onthIJ8 true1 localiPed5rra"HIchangeGearIJ8 true1 localiPed5rra"HIshoC'nIJ 8 IbuttonI1 localiPed5rra"HIbutton$mage'nl"IJ 8 true1 &1
=unction init.H:atepicker ta#es in input the following parameters( input!d( the id of the 9T<: te,t field corresponding to our custom component instance. countr !so.ode( a two-letter low-case -"@ language code. -t can contain also the two-letter upper-case -"@ country code separated with a minus sign (for e,ample en--.) dateHormat( the date format to use for parsing and displaying date .alues. calendar!con( the relati.e &+: of the icon used as calendar icon. As we will see in the ne,t paragraphs* its up to our component to generate this parameters and in.o#e the init.H:atepicker function. =unction initCalendar is a simple utility function that sets the initialiCation array for datepic#er widget. =or more details on KQuery &- datepic#er usage see the documentation at http(//HEueryui.com/ datepic#er.
1#.2.2
!nitiali6ation code
The initialiCation code for our component is contained inside its method on2nitiali3e and is the following(
!'4erride protected 4oid on$nitialiPe7: super.on$nitialiPe7:1 set'utput;arkup$d7true:1 date#attern 8 neC *esource;odel792Euer"+ate3ield.short+ate#attern9, 9mm/dd/""9: .get'b2ect7:1 countr"$soCode 8 neC *esource;odel792Euer"+ate3ield.countr"$soCode9, 9en--.9: .get'b2ect7:1 #ackage*esource*e%erence resource*e%erence 8 neC #ackage*esource*e%erence7getClass7:, 9calendar.2pg9:1 url3or$con 8 url3or7resource*e%erence, neC #age#arameters7::1 dateCon4erter 8 neC #attern+ateCon4erter7date#attern, %alse:1 & !'4erride public <+ate> $Con4erter<+ate> getCon4erter7Class<+ate> t"pe: return 7$Con4erter<+ate>: dateCon4erter1 &
134
The first thing to do inside on2nitiali3e is to ensure that our component will ha.e a mar#up id for its related te,t field. This is done in.o#ing set+"tp"tMark"p2d(tr"e . e,t* .H"er,:ateField tries to retrie.e the date format and the -"@ language code that must be used as initialiCation parameters. This is done using class )eso"rceModel which searches for a gi.en resource in the a.ailable bundles. -f no .alue is found for date format or for -"@ language code* default .alues will be used (Bmm/dd/""B and Ben--.B). To generate the relati.e &+: for calendar icon* we load it as pac#age resource reference and then we use ComponentBs method "rlFor to get the &+: .alue (we ha.e seen this method in paragraph 6.$.4). The last configuration instruction e,ecuted inside on2nitiali3e is the instantiation of the custom con.erter used by our component. This con.erter is an instance of the built-in class org.apache. wicket.datetime.Pattern:ateCon!ert and must use the pre.iously retrie.ed date format to perform con.ersion operations. ow to tell our component to use this con.erter we must return it o.erriding FormComponentBs method getCon!erter.
1#.2.3
The rest of the code of our custom component is inside method renderHeader* which is responsible for adding to page header the bundled KQuery library* the three files from KQuery &- distribution* the custom file KQDatePic#er.Hs and the in.ocation of function init.H:atepicker(
!'4erride public 4oid renderDead7$Deader*esponse response: super.renderDead7response:1 //i% component is disabled Ce donIt ha4e to load the LVuer"0$ datepicker i%7Fis(nabled$nDierarch"7:: return1 //add bundled LVuer" $La4a)cript/ibrar")ettings 2a4a)cript)ettings 8 get5pplication7:.getLa4a)cript/ibrar")ettings7:1 response.render7La4a)criptDeader$tem.
%or*e%erence72a4a)cript)ettings.getLVuer"*e%erence7:::1
//add package resources response.render7La4a)criptDeader$tem.
-f component is disabled the calendar icon must be hidden and no datepic#er must be displayed. ThatBs why renderHeader is s#ipped if component is not enabled. To get a reference to the bundled KQuery library we used the Ka.a"cript setting interface 2.a!a
13$
ScriptLibrar,Settings and its method get.H"er,)e0erence. -n the last part of renderHeader we build the string to in.o#e function init.H:atepicker using the .alues obtained inside on2nitiali3e. &nfortunately the date format used by KQuery &- is different from the one adopted in Ka.a so we ha.e to con.ert it before building the Ka.a"cript code. This init script is rendered into header section using a +nLoadHeader2tem to ensure that it will be e,ecuted after all the other scripts ha.e been loaded. >ote -f we add more than one instance of our custom component to a single page* static resources are rendered to the header section Hust once. 2ic#et automatically chec#s if a static resource is already referenced by a page and if so* it will not render it again. This does not apply to the init script which is dynamically generated and is rendered for e.ery instance of the component. Warning @ur datepic#er is not ready yet to be used with AKAL. -n chapter 1A we will see how to modify it to ma#e it AKAL-compatible.
1#.3 8ummar
-n this brief chapter we ha.e seen how custom components can be integrated with D9T<: 36 technologies. To do so we ha.e used most of what we ha.e learnt in this guide. ow we are able to build comple, components with a rich user e,perience. 9owe.er this is not enough yet to de.elop 2eb 4.%37 applications. 2e still ha.e to co.er a fundamental technology li#e AKAL and some other 2ic#etrelated topics that will help us building our application in more modular and efficient way.
133
135
i%7componet instanceo% 3ormComponent UU 773ormComponent:component:.is*eEuired7:: asteriskDtml.append79 <b st"le8\9color:red1%ont-siPe:medium\9>*</b>9:1 & response.Crite7asteriskDtml:1 & &
"ince method be0ore)ender is called before the coupled component is rendered* we can use it to prepend custom mar#up to component tag. This can be done writing our mar#up directly to the current )esponse obHect* as we did in the e,ample abo.e. Please note that we could achie.e the same result o.erriding component method on1e0ore)ender. 9owe.er using a beha.ior we can easily reuse our custom code with any other #ind of component without modifying its source code. As general best practice we should always consider to implement a new functionality using a beha.ior if it can be shared among different #inds of component. Beha.iors play also a strategic role in the built-in AKAL support pro.ided by 2ic#et* as we will see in the ne,t chapter.
To control how the method will be in.o#ed we must use class org.apache.wicket.)e6"est Listener2nter0ace. -n 2ic#et is a common practice to instantiate this class as a public static field inside the relati.e callbac# interface(
public inter%ace $;"/istener extends $*eEuest/istener /***eEuest/istener$nter%ace instance*/ public static %inal *eEuest/istener$nter%ace $NT(*35C( 8 neC *eEuest/istener$nter%ace7$;"/istener.class:1 /** * Called Chen the relati4e callback 0*/ is reEuested. */ 4oid m"Callback;ethod7:1 &
By default )e6"estListener2nter0ace will respond rendering the current page after the callbac# method has been e,ecuted (if we ha.e a non-AKAL reEuest). To change this beha.ior we can use setter
49 7 functional interface is an an interface that defines #ust one method$
13A
method set)enderPageA0ter2n!ocation(boolean . ow that our callbac# interface is complete we can generate a callbac# &+: with ComponentBs method "rlFor()e6"estListener2nter0ace4 PageParameters or with method "rlFor (1eha!ior4 )e6"estListener2nter0ace4 PageParameters if we are using a callbac# interface with a beha.ior (see the following e,ample). ProHect (allback:"%#0ample contains a beha.ior (class +nChangeSingleChoice1eha!ior) that implements a callbac# interface to update the model of an AbstractSingleSelectChoice component when user changes the selected option (it pro.ides the same functionality of method want +nSelectionChangedNoti0ications). -nstead of a custom callbac# interface* +nChangeSingleChoice1eha!ior implements built-in interface org.apache.wicket.beha!ior.21eha!iorListener which is designed to generate a callbac# &+: for beha.iors. The callbac# method defined in this interface is on)e6"est( and the following is the implementation pro.ided by +nSelectionChangedNoti0ications(
!'4erride public 4oid on*eEuest7: *eEuest reEuest 8 *eEuestC"cle.get7:.get*eEuest7:1 $*eEuest#arameters reEuest#arameters 8 reEuest.get*eEuest#arameters7:1 )tringOalue choice$d 8 reEuest#arameters.get#arameterOalue79choice$d9:1 //boundComponent is the component that the beha4ior it is bound to. boundComponent.set+e%ault;odel'b2ect7con4ertChoice$dToChoice7choice$d.to)tring7:::1 &
2hen in.o#ed .ia &+:* the beha.ior e,pects to find a reEuest parameter ( choice$d) containing the id of the selected choice. This .alue is used to obtain the corresponding choice obHect that must be used to set the model of the component that the beha.ior is bound to ( boundComponent). <ethod con!ertChoice2d/oChoice is in charge of retrie.ing the choice obHect gi.en its id and it has been copied from class AbstractSingleSelectChoice. Another interesting part of +nChangeSingleChoice1eha!ior is its method onComponent/ag where some Ka.a"cript FmagicG is used to mo.e userBs browser to the callbac# &+: when e.ent FchangeG occurs on bound component(
!'4erride public 4oid onComponentTag7Component component, ComponentTag tag: super.onComponentTag7component, tag:1 Char)eEuence call.ack0*/ 8 getCallback0rl7:1 )tring separatorChar 8 7call.ack0*/.to)tring7:.index'%7I?I: > -@ ? 9U9 : 9?9:1 )tring %inal)cript 8 94ar is)elect 8 W7this:.is7IselectI:1\n9 > 94ar component1\n9 > 9i%7is)elect:\n9 > 9 9 component 8 W7this:1\n9 > component 8 W7this:.%ind7Iinput:radio:checkedI:1\n9 > separatorChar > 9else \n9 > 9CindoC.location.hre%8I9 > call.ack0*/ > 9choice$d8I > 9 > 9component.4al7:91 tag.put79onchange9, %inal)cript:1 &
onchange
the callbac# &+: (modifing standard property CindoC.location.hre%). Please note that we ha.e appended the e,pected parameter (choice$d) to the &+: retrie.ing its .alue with a KQuery selector suited for the current type of component (a drop-down menu or a radio group). "ince we are using KQuery in our Ka.a"cript code* the beha.ior comes also with method renderHead that adds the bundled KQuery library to the current page. <ethod getCallback9rl( is used to generate the callbac# &+: for our custom beha.ior and it has been copied from built-in class AbstractA5a(1eha!ior(
public Char)eEuence getCallback0rl7: i% 7boundComponent 88 null: throC neC $llegal5rgument(xception7 9.eha4ior must be bound to a component to create the 0*/9:1 & %inal *eEuest/istener$nter%ace rli1 rli 8 $.eha4ior/istener.$NT(*35C(1 return boundComponent.url3or7this, rli, neC #age#arameters7::1 &
"tatic field 21eha!iorListener.2N/')FAC' is the implementation of )e6"estListener 2nter0ace defined inside callbac# interface 21eha!iorListener. The home page of proHect (allback:"%#0ample contains a :rop:ownChoice and a )adioChoice which use our custom beha.ior. There are also two labels to display the content of the models of the two components(
>ote -mplementing interface 21eha!iorListener ma#es a beha.ior stateful because its callbac# &+: is specific for a gi.en instance of component.
137
application obHect and the current reEuest cycle. -t has four possible .alues( Ealue B+IADT9 DIPT9 5escription The e.ent is sent first to the specified sin# and then to all its children components following a breadth-first order. The e.ent is sent to the specified sin# only after it has been dispatched to all its children components following a depth-first order. The e.ent is sent first to the specified sin# and then to its parent containers. The e.ent is sent only to the specified sin#.
B&BB:I ILA!T
pa load( a generic obHect representing the data sent with the e.ent.
Iach broadcast mode has its own tra.ersal order for Session4 )e6"estC,cle and Application. "ee Ka.aDoc of class 1roadcast for further details about this order. -nterface 2'!entSink e,poses callbac# method on'!ent(2'!ent7D8 e!ent which is triggered when a sin# recei.es an e.ent. The interface 2'!ent represents the recei.ed e.ent and pro.ides getter methods to retrie.e the e.ent broadcast type* the source of the e.ent and its payload. Typically the recei.ed e.ent is used chec#ing the type of its payload obHect (
!'4erride public 4oid on(4ent7$(4ent e4ent: //i% the t"pe o% pa"load is ;"#a"loadClass per%orm some actions i%7e4ent.get#a"load7: instanceo% ;"#a"loadClass: //execute some business code. &else //other business code & &
ProHect Inter(omponets#vents#0ample pro.ides a concrete e,ample of sending an e.ent to a component (named Bcontainer in the middleB) using all the a.ailable broadcast methods(
llustration 19$1% Pro#ect nterComponentsHventsHxample displaying the order in which the entities are notified using broadcast type B@BB0H
138
1(.# !nitiali6ers
"ome components or resources may need to be configured before being used in our applications. 2hile so far we used ApplicationBs init method to initialiCe these #inds of entities* 2ic#et offers a more fle,ible and modular way to configure our classes. During applicationBs bootstrap 2ic#et searches for any properties file named wicket.properties placed in one of the classpath roots .isible to the application 5%. 2hen one of these files is found* the initiali<er defined inside it will be e,ecuted. An initialiCer is an implementation of interface org.apache.wicket.22nitiali3er and is defined inside wicket.properties with a line li#e this(
initialiPer8org.CicketTutorial.;"$nitialiPer
The fully Eualified class name corresponds to the initialiCer that must be e,ecuted. -nterface 22nitiali3er defines method init(Application which should contain our initialiCation code* and method destro,(Application which is in.o#ed when application is terminated(
public class ;"$nitialiPer implements $$nitialiPer public 4oid init75pplication application: //initialiPation code & public 4oid destro"75pplication application: //code to execute Chen application is terminated & &
@nly one initialiCer can be defined in a single wicket.properties file. To o.ercome this limit we can create a main initialiCer that in turn e,ecutes e.ery initialiCer we need(
public class ;ain$nitialiPer implements $$nitialiPer public 4oid init75pplication application: neC 5nother$nitialiPer7:.init7application:1 neC Get5nother$nitialiPer7:.init7application:1 //... & //destro"... &
50
This set of classpath roots includes the root of the package of the application class and the roots of the visible )7As
15%
After we ha.e established a K<L connection* K!onsole will show us the following set of tabs
K<L e,poses application-specific informations using special obHects called <Beans ( 4anageable 9eans)* hence if we want to control our application we must open the corresponding tab. The <Beans containing the applicationBs informations is named org.apache.Cicket.app.<%ilter/ser4let name>. -n our e,ample we ha.e used Cicket.test as filter name51 for our application(
151
As we can see in the picture abo.e* e.ery <Bean e,poses a node containing its attributes and another node showing the possible operations that can be performed on the obHect. -n the case of a 2ic#et application the a.ailable operations are clearMark"pCache and clearLocali3erCache(
2ith these two operations we can force 2ic#et to clear the internal caches used to load components mar#up and resource bundles. This can be particularly useful if we ha.e our application running in DIP:@'<I T mode and we want to publish minor fi,es for mar#up or bundle files (li#e spelling or typo corrections) without restarting the entire application. 2ithout cleaning these two caches 2ic#et would continue to use cached .alues ignoring any change made to mar#up or bundle files. "ome of the e,posed properties are editable* hence we can tune their .alues while the application is running. =or e,ample if we loo# at the properties of ApplicationSettings we can set the ma,imum siCe allowed for an upload modifying the attribute :e0a"ltMa(im"m9ploadSi3e(
154
<di4> tag
as mar#up(
public class 5uto;arkup-en#anel extends #anel implements $;arkup*esource)tream#ro4ider public 5uto;arkup-en#anel7)tring id, $;odel<?> model: super7id, model:1 & !'4erride public $*esource)tream get;arkup*esource)tream7;arkupContainer container, Class<?> containerClass: )tring markup 8 9<di4>#anel markup</di4>91 )tring*esource)tream resource)tream 8 neC )tring*esource)tream7markup:1 return resource)tream1 & &
!lass String)eso"rceStream is a resource stream that uses a String instance as bac#ing obHect.
1(.-.1
As we ha.e seen in the pre.ious paragraph* 2ic#et uses an internal cache for components mar#up. This can be a problem if our component dynamical generates its mar#up when it is rendered because once the mar#up has been cached* 2ic#et will always use the cached .ersion for the specific component. To o.erwrite this default caching policy* a component can implement interface 2Mark"pCache;e,Pro!ider. This interface defines method getCache;e,(Mark"pContainer4 Class7D8 which returns a string .alue representing the #ey used by 2ic#et to retrie.e the mar#up of the component from the cache. -f this .alue is n"ll the mar#up will not be cached* allowing the component to display the last generated mar#up each time it is rendered(
public class NoCache;arkup#anel extends #anel implements $;arkupCache6e"#ro4ider public NoCache;arkup#anel7)tring id, $;odel<?> model: super7id, model:1 & /** * -enerate a d"namic DT;/ markup that changes e4er" time * the component is rendered */ !'4erride public $*esource)tream get;arkup*esource)tream7;arkupContainer container, Class<?> containerClass: )tring markup 8 9<di4>#anel Cith current nanotime: 9 > )"stem.nanoTime7: > 9</di4>91 )tring*esource)tream resource)tream 8 neC )tring*esource)tream7markup:1 return resource)tream1 & /** * 54oid markup caching %or this component */
15$
!'4erride public )tring getCache6e"7;arkupContainer argA, Class<?> arg@: return null1 & &
1(.' 8ummar
-n this chapter we ha.e introduced some ad.anced topics we didnBt ha.e the chance to co.er yet. 2e ha.e started tal#ing about beha.iors and we ha.e seen how they can be used to enrich e,isting components (promoting a component-oriented approach). Beha.iors are also fundamental to wor# with AKAL in 2ic#et* as we will see in the ne,t chapter. After beha.iors we ha.e learnt how to generate callbac# &+:s to e,ecute a custom method on ser.er side defined inside a specific callbac# interface. The third topic of the chapter has been the e.ent infrastructure pro.ided in 2ic#et for inter-component communication which brings to our components a des#top-li#e e.ent-dri.en architecture. Then* we ha.e introduced a new entity called initialiCer which can be used to configure resources and component in a modular and self-contained way. 2e ha.e also loo#ed at 2ic#et support for K<L and we ha.e seen how to use this technology for monitoring and managing our running applications. =inally we ha.e introduced a new techniEue to generate the mar#up of a component from its Ka.a code.
153
&sing A5a()e6"est/arget we can specify the content that must be sent bac# to the client as response to the current AKAL reEuest. The most commonly used method of this interface is probably add(Component... components . 2ith this method we tell 2ic#et to render again the specified components and refresh their mar#up .ia AKAL(
neC 52ax/ink79a2ax/ink9: !'4erride public 4oid onClick752ax*eEuestTarget target: //modi%" the model o% a label and re%resh it on broCser label.set+e%ault;odel'b2ect795nother 4alue < label.9:1 target.add7label:1 & &1
!omponents can be refreshed .ia AHa, only if they ha.e rendered a mar#up id for their related tag. As a conseEuence* we must remember to set a .alid id .alue on e.ery component we want to add to A5a()e6"est/arget. This can be done using one of the two methods seen in paragraph 3.$(
155
%inal /abel label 8 neC /abel79labelComponent9, 9$nitial 4alue.9:1 //autogenerate a markup id label.set'utput;arkup$d7 true:1 add7label:1 //... neC 52ax/ink79a2ax/ink9: !'4erride public 4oid onClick752ax*eEuestTarget target: //modi%" the model o% a label and re%resh it on client side label.set+e%ault;odel'b2ect795nother 4alue < label.9:1 target.add7label:1 & &1
Another common use of A5a()e6"est/arget is to prepend or append some Ka.a"cript code to the generated response. =or e,ample the following AKAL lin# displays an alert bo, as response to userBs clic#(
neC 52ax/ink79a2ax/ink9: !'4erride public 4oid onClick752ax*eEuestTarget target: target.appendLa4a)cript791alert7IDelloFFI:19:1 & &1
Warning +epeaters component that ha.e org.apache.wicket.mark"p.repeater Abstract)epeater as base class (li#e List-iew* )epeating-iew* etc...) can not be directly updated .ia AKAL. if we want to refresh their mar#up .ia AKAL we must add one of their parent containers to the A5a()e6"est/arget.
1-.2.1
-n the pre.ious paragraph we ha.e already introduced component A5a(Link. 2ic#et pro.ides also the aHa,ified .ersions of submitting components S"bmitLink and 1"tton which are simply called A5a(S"bmitLink and A5a(1"tton. These components come with a .ersion of methods onS"bmit* on'rror and onA0terS"bmit that ta#es in input also an instance of A5a()e6"est/arget. Both components are in pac#age org.apache.wicket.a5a(.mark"p.html.0orm.
1-.2.2
Hall=ack components
15A
Building an entire site using AKAL can be ris#y as some clients may not support this technology. -n order to pro.ide an usable .ersion of our site also to these clients* we can use components A5a(FallbackLink and A5a(Fallback1"tton which are able to automatically degrade to a standard lin# or to a standard button if client doesnBt support AKAL.
1-.2.3
A7A9 .heck=ox
!lass org.apache.wicket.a5a(.mark"p.html.0orm.A5a(Check1o( is a chec#bo, component that updates its model .ia AKAL when user changes its .alue. -ts AKAL callbac# method is on9pdate(A5a()e6"est/arget target . The component e,tends standard chec#bo, component Check1o( adding an A5a(FormComponent9pdating1eha!ior to itself (we will see this beha.ior later in paragraph 1A.$.$).
1-.2.#
An editable label is a special label that can be edited by the user when she/he clic#s on it. 2ic#et ships three different implementations for this component (all inside pac#age org.apache.wicket. e(tensions.a5a(.mark"p.html)( A<axDdita=le,a=el( itBs a basic .ersion of editable label. &ser can edit the content of the label with a te,t field. This is also the base class for the other two editable labels. A<axDdita=le+ulti,ine,a=el( this label supports multi-line .alues and uses a te,t area as editor component. A<axDdita=le.hoice,a=el( this label uses a drop-down menu to edit its .alue. Base component A5a('ditableLabel e,poses the following set of AKAL-aware methods that can be o.erriden( onDditJA<ax3e1uest*arget target@( called when user clic#s on component. The default implementation shows the component used to edit the .alue of the label. on8u=mitJA<ax3e1uest*arget target@( called when the .alue has been successfully updated with the new input. onDrrorJA<ax3e1uest*arget target@( called when the new inserted input has failed .alidation. on.ancelJA<ax3e1uest*arget target@( called when user has e,ited from editing mode pressing escape #ey. The default implementation brings bac# the label to its initial state hiding the editor component. 2ic#et module wicket@e(amples contains page class 'ditableLabelPage.5a!a which shows all these three components together. 'ou can see this page in action at http(//www.wic#etlibrary.com/wic#et-e,amples-A.%.,/aHa,/editable-label(
156
1-.2.(
@n -nternet we can find many e,amples of te,t fields that display a list of suggestions (or options) while the user types a te,t inside them. This feature is #nown as autocomplete functionality. 2ic#et offers an out-of-the-bo, implementation of an autocomplete te,t field with component org.apache.wicket.e(tensions.a5a(.mark"p.html.a"tocomplete.A"toComplete/e(tFi eld. 2hen using A"toComplete/e(tField we are reEuired to implement its abstract method getChoices(String inp"t where the inp"t parameter is the current input of the component. This method returns an iterator o.er the suggestions that will be displayed as a drop-down menu(
llustration 18$*% The autocomplete example bundled with Wicket "uggestions are rendered using a render which implements interface 2A"toComplete)enderer. The default implementation simply calls toString( on each suggestion obHect. -f we need to wor# with a custom render we can specify it .ia component constructor. A"toComplete/e(tField supports a wide range of settings that are passed to its constructor with class A"toCompleteSettings. @ne of the most interesting parameter we can specify for A"toComplete/e(tField is the throttle delay which is the amount of time (in milliseconds) that must elapse between a change of input .alue and the transmission of a new AHa, reEuest to display suggestions. This parameter can be set with method set/hrottle:ela,(int (
5utoComplete)ettings settings 8 neC 5utoComplete)ettings7:1 //set throttle to <AA ms: component Cill Cait <AAms be%ore displa"ing the options settings.setThrottle+ela"7<AA:1 //... 5utoCompleteText3ield %ield 8 neC 5utoCompleteText3ield<T>79%ield9, model: !'4erride protected $terator getChoices7)tring argA: //return an iterator o4er the options & &1
2ic#et module wicket@e(amples contains page class A"toCompletePagePage.5a!a which shows an e,ample of autocomplete te,t field. The running e,ample is a.ailable at http(//www.wic#etlibrary.com/wic#et-e,amples-A.%.,/aHa,/autocomplete.
1-.2.-
+odal window
157
llustration 18$1% The modal window from pro#ect Basic/odalWindowHxample The content of a modal window can be either another component or a page. -n the first case the id of the component used as content must be retrie.ed with method getContent2d( . -f instead we want to use a page as window content* we must implement the inner interface ModalWindow.Page(reator and pass it to method setPageCreator. The page used as content will be embedded in a <i%rame> tag. To display a modal window we must call its method show(A5a()e6"est/arget target . This is usually done inside the AKAL callbac# method of another component (li#e an A5a(Link). The following mar#up and code are ta#en from proHect 9asic4odal.indow#0ample and illustrate a basic usage of a modal window( 9tml(
<bod"> <h=>;odal Windod example</h=> <a Cicket:id89openWindoC9>'pen the CindoCF</a> <di4 Cicket:id89modalWindoC9></di4> </bod">
Ka.a code(
public Dome#age7%inal #age#arameters parameters: super7parameters:1 %inal ;odalWindoC modalWindoC 8 neC ;odalWindoC79modalWindoC9:1 /abel label 8 neC /abel7modalWindoC.getContent$d7:, 9$Im a modal CindoCF9:1 modalWindoC.setContent7label:1 modalWindoC.setTitle79;odal CindoC9:1 add7modalWindoC:1 add7neC 52ax/ink79openWindoC9: !'4erride public 4oid onClick752ax*eEuestTarget target: modalWindoC.shoC7target:1 &
53 http: en.wikipedia.org wiki 4odal1window
158
&:1 &
Kust li#e any other component also ModalWindow must be added to a mar#up tag* li#e we did in our e,ample using a <di4> tag. 2ic#et will automatically hide this tag in the final mar#up appending the style .alue displa":none. The component pro.ides different setter methods to customiCe the appearance of the window( set*itleJ8tring@( specifies the title of the window set3esi6a=leJ=oolean@( by default the window is resiCeable. -f we need to ma#e its siCe fi,ed we can use this method to turn off this feature. set!nitialWidthJint@ and set!nitialHeightJint@( set the initial dimensions of the window. set+inimalWidthJint@ and set+inimalHeightJint@( specify the minimal dimensions of the window. set.ookie>ameJ8tring@( this method can be used to specify the name of the coo#ie used on client side to store siCe and position of the window when it is closed. The component will use this coo#ie to restore these two parameters the ne,t time the window will be opened. -f no coo#ie name is pro.ided* the component will not remember its last position and siCe. set.ss.lass>ameJ8tring@( specifies the !"" class used for the window. setAuto8i6eJ=oolean@( when this flag is set to tr"e the window will automatically adHust its siCe to fit content width and height. By default it is 0alse. The modal window can be closed from code using its method close(A5a()e6"est/arget target . The currently opened window can be closed also with the following Ka.a"cript instruction(
Wicket.WindoC.get7:.close7:1
ModalWindow gi.es the opportunity to perform custom actions when window is closing. -nner interface ModalWindow..indow(losed(allback can be implemented and passed to windowBs method setWindowClosedCallback to specify the callbac# that must be e,ecuted after window has been closed(
modalWindoC.setWindoCClosedCallback7neC ;odalWindoC.WindoCClosedCallback7: !'4erride public 4oid onClose752ax*eEuestTarget target: //custom code... & &:1
1-.2.'
*ree repeaters
!lass org.apache.wicket.e(tensions.mark"p.html.repeater.tree.Abstract/ree is the base class of another family of repeaters called tree repeaters and designed to display a data hierarchy as a tree* resembling the beha.ior and the loo# Y feel of des#top tree components. A classic e,ample of tree component on des#top is the tree used by nearly all file managers to na.igate file system(
1A%
llustration 18$3% (ile system tree on Windows ?P 4on the left5 and @buntu 4on the right5 Because of their highly interacti.e nature* tree repeaters are implemented as AKAL components* meaning that they are updated .ia AKAL when we e,pand or collapse their nodes. The basic implementation of a tree repeater shipped with 2ic#et is component Nested/ree. -n order to use a tree repeater we must pro.ide an implementation of interface 2/reePro!ider which is in charge of returning the nodes that compose the tree. 2ic#et comes with a built-in implementation of 2/reePro!ider called /reeModelPro!ider that wor#s with the same tree model 53 and nodes used by "wing component 5a!a(.swing../ree. These "wing entities should be familiar to you if you ha.e pre.iously wor#ed with the old tree repeaters (components /ree and /ree/able) that ha.e been deprecated with 2ic#et A and that are strongly dependent on "wing-based model and nodes. /reeModelPro!ider can be used to migrate your code to the new tree repeaters. -n the ne,t e,ample (proHect (heck9o0$-a0!ree) we will build a tree that displays some of the main cities of three Iuropean countries( -taly* 0ermany and =rance. The cities are sub-nodes of a main node representing the relati.e county. The nodes of the final tree will be also selectable with a chec#bo, control. The whole tree will ha.e the classic loo# Y feel of 2indows LP. This is how our tree will loo# li#e(
llustration 18$9% The tree repeater from pro#ect CheckBox7#axTree 2e will start to e,plore the code of this e,ample from the home page. The first portion of code we will see is where we build the nodes and the /reeModelPro!ider for the three. As tree node we will use "wing class 5a!a(.swing.tree.:e0a"ltM"table/reeNode(
public class Dome#age extends Web#age public Dome#age7%inal #age#arameters parameters:
54 Tree model is an implementation of interface #avax$swing$tree$Tree/odel and it has nothing to do with Wicket models,
1A1
addNodes7addNodes7root, 9$tal"9:, 9*ome9, 9Oenice9, 9;ilan9, 93lorence9:1 addNodes7addNodes7root, 9-erman"9:,9)tuttgart9,9;unich9,9.erlin9,9+usseldor%9, 9+resden9:1 addNodes7addNodes7root, 93rance9:, 9#aris9,9Toulouse9,9)trasbourg9,9.ordeaux9, 9/"on9:1
+e%aultTree;odel tree;odel 8 neC +e%aultTree;odel7root:1 Tree;odel#ro4ider<+e%ault;utableTreeNode> model#ro4ider 8 neC Tree;odel#ro4ider<+e%ault;utableTreeNode>7tree;odel: !'4erride public $;odel<+e%ault;utableTreeNode> model7+e%ault;utableTreeNode ob2ect: return ;odel.o%7ob2ect:1 & &1 //To be continued...
odes ha.e been built using simple strings as data obHects and in.o#ing custom utility method addNodes which con.erts string parameters into children nodes for a gi.en parent node. @nce we ha.e our tree of :e0a"ltM"table/reeNodes we can build the "wing tree model ( :e0a"lt/reeModel) that will be the bac#ing obHect for a /reeModelPro!ider. This pro.ider wraps each node in a model in.o#ing its abstract method model. -n our e,ample we ha.e used a simple Model as wrapper model. "crolling down the code we can see how the tree component is instantiated and configured before being added to the home page(
//Continued %rom pre4ious snippet... NestedTree<+e%ault;utableTreeNode> tree 8 neC NestedTree<+e%ault;utableTreeNode>79tree9, model#ro4ider:
!'4erride protected Component neCContentComponent7)tring id, $;odel<+e%ault;utableTreeNode>model: return neC Checked3older<+e%ault;utableTreeNode>7id, this, model:1 & &1 //select WindoCs theme tree.add7neC WindoCsTheme7::1 add7tree:1 & //implementation o% addNodes //... &
To use tree repeaters we must implement their abstract method newContentComponent which is called internally by base class Abstract/ree when a new node must be built. As content component we ha.e used built-in class CheckedFolder which combines a Folder component with a Check1o( form control. The final step before adding the tree to its page is to apply a theme to it. 2ic#et comes with two beha.iors* Windows/heme and H"man/heme* which correspond to the classic 2indows LP theme and to the 9uman theme from &buntu.
1A4
@ur chec#able tree is finished but our wor# is not o.er yet because the component doesnBt offer many functionalities as it is. &nfortunately neither Nested/ree nor CheckedFolder pro.ide a means for collecting chec#ed nodes and returning them to client code. -tBs up to us to implement a way to #eep trac# of chec#ed nodes. Another nice feature we would li#e to implement for our tree is the following user-friendly beha.ior that should occur when a user chec#s/unchec#s a node( 2hen a node is chec#ed also all its children nodes (if any) must be chec#ed. 2e must also ensure that all the ancestors of the chec#ed node (root included) are chec#ed* otherwise we would get an inconsistent selection. 2hen a node is unchec#ed also all its children nodes (if any) must be unchec#ed and we must also ensure that ancestors get unchec#ed if they ha.e no more chec#ed children. The first goal (#eeping trac# of chec#ed node) can be accomplished building a custom .ersion of CheckedFolder that uses a shared Ka.a Set to store chec#ed node and to .erify if its node has been chec#ed. This #ind of solution reEuires a custom model for chec#bo, component in order to reflect its chec#ed status when its container node is rendered. This model must implement typed interface 2Model71oolean8 and must be returned by CheckedFolderBs method newCheck1o(Model. =or the second goal (auto select/unselect children and ancestor nodes) we can use CheckedFolderBs callbac# method on9pdate(A5a()e6"est/arget that is in.o#ed after a chec#bo, is clic#ed and its .alue has been updated. @.erriding this method we can handle user clic# adding/remo.ing nodes to/from the Ka.a Set. =ollowing this implementation plan we can start coding our custom CheckedFolder (named A"tocheckedFolder)(
public class 5utochecked3older<T> extends Checked3older<T> pri4ate $Tree#ro4ider<T> tree#ro4ider1 pri4ate $;odel<)et<T>> checkedNodes1 pri4ate $;odel<.oolean> checkbox;odel1 public 5utochecked3older7)tring id, 5bstractTree<T> tree, $;odel<T> model, $;odel<)et<T>> checkedNodes: super7id, tree, model:1 this.tree#ro4ider 8 tree.get#ro4ider7:1 this.checkedNodes 8 checkedNodes1 & !'4erride protected $;odel<.oolean> neCCheck.ox;odel7$;odel<T> model: checkbox;odel 8 & !'4erride protected 4oid on0pdate752ax*eEuestTarget target: super.on0pdate7target:1 T node 8 get;odel'b2ect7:1 boolean nodeChecked 8 checkbox;odel.get'b2ect7:1 add*emo4e)ubNodes7node, nodeChecked:1 add*emo4e5ncestorNodes7node, nodeChecked:1 & neC Check;odel7:1 return checkbox;odel1
1A$
class Check;odel extends 5bstractCheck.ox;odel !'4erride public boolean is)elected7: return checkedNodes.get'b2ect7:.contains7get;odel'b2ect7::1 & !'4erride public 4oid select7: checkedNodes.get'b2ect7:.add7get;odel'b2ect7::1 & !'4erride public 4oid unselect7: checkedNodes.get'b2ect7:.remo4e7get;odel'b2ect7::1 & & &
The constructor of this new component ta#es in input a further parameter which is the set containing chec#ed nodes. !lass CheckModel is the custom model we ha.e implemented for chec#bo, control. As base class for this model we ha.e used AbstractCheck1o(Model which is pro.ided to implement custom models for chec#bo, controls. <ethods add)emo!eS"bNodes and add)emo!eAncestorNodes are called to automatically add/remo.e children and ancestor nodes to/from the current Set. Their implementation is mainly focused on the na.igation of tree nodes and it hea.ily depends on the internal implementation of the tree* so we wonBt dwell on their code. ow we are Hust one step away from completing our tree as we still ha.e to find a way to update the chec#ed status of both children and ancestors nodes on client side. Although we could easily accomplish this tas# by simply refreshing the whole tree .ia AKAL* we would li#e to find a better and more performant solution for this tas#. 2hen we modify the chec#ed status of a node we donBt e,pand/collapse any node of the three so we can simply update the desired chec#bo,es rather than updating the entire tree component. This alternati.e approach could lead to a more responsi.e interface and to a strong reduction of bandwidth consumption. 2ith the help of KQuery we can code a couple of Ka.a"cript functions that can be used to chec#/ unchec# all the children and ancestors of a gi.en node. Then* we can append these functions to the current A5a()e6"est at the end of method on9pdate(
!'4erride protected 4oid on0pdate752ax*eEuestTarget target: super.on0pdate7target:1 T node 8 get;odel'b2ect7:1 boolean nodeChecked 8 checkbox;odel.get'b2ect7:1 add*emo4e)ubNodes7node, nodeChecked:1 add*emo4e5ncestorNodes7node, nodeChecked:1 updateNode'nClient)ide7target, nodeChecked:1 &
1A3
protected 4oid updateNode'nClient)ide752ax*eEuestTarget target, boolean nodeChecked: target.appendLa4a)cript791Check5ncestors5ndChildren.checkChildren7I9 > get;arkup$d7: > 9I,9 > nodeChecked > 9:19:1 target.appendLa4a)cript791Check5ncestors5ndChildren.check5ncestors7I9 > get;arkup$d7: > 9I,9 > nodeChecked > 9:19:1 &
The Ka.a"cript code can be found inside file section as pac#age resource(
!'4erride
autochecked3older.2s
public 4oid renderDead7$Deader*esponse response: #ackage*esource*e%erence script3ile 8 neC #ackage*esource*e%erence7this.getClass7:, 9autochecked3older.2s9:1 response.render7La4a)criptDeader$tem.%or*e%erence7script3ile::1 &
1-.2.2
2hen a component is not .isible its mar#up and the related id attribute are not rendered in the final page* hence it can not be updated .ia AKAL. To o.ercome this problem we must use ComponentBs method set+"tp"tMark"pPlaceholder/ag(tr"e which has the effect of rendering a hidden <span> tag containing the mar#up id of the hidden component(
%inal /abel label 8 neC /abel79labelComponent9, 9$nitial 4alue.9:1 //make label in4isible label.setOisible7%alse:1 //ensure that label Cill lea4e a placeholder %or its markup id label.set'utput;arkup#laceholderTag7 true:1 add7label:1 //... neC 52ax/ink79a2ax/ink9: !'4erride public 4oid onClick752ax*eEuestTarget target: //turn label to 4isible label.setOisible7true:1 target.add7label:1 & &1
Please note that in the code abo.e we didnBt in.o#ed method set+"tp"tMark"p2d(tr"e set+"tp"tMark"pPlaceholder/ag already does it internally.
as
1-.3.1
A<axDvent;ehavior
1A5
A5a('!ent1eha!ior allows to handle a Ka.a"cript e.ent (li#e clic#* change* etc...) on ser.er side .ia AKAL. -ts constructor ta#es in input the name of the e.ent that must be handled. I.ery time this e.ent is fired for a gi.en component on client side* the callbac# method on'!ent(A5a()e6"est/arget target is e,ecuted. on'!ent is abstract* hence we must implement it to tell A5a('!ent1eha!ior what to do when the specified e.ent occurs. -n proHect $-a0#vent9ehavior#0ample we used this beha.ior to build a Fclic#ableG Label component that counts the number of clic#s. 9ere is the code from the home page of the proHect( 9tml(
<bod"> <di4 Cicket:id89clickCounter/abel9></di4> 0ser has clicked <span Cicket:id89clickCounter9></span> time/s on the label abo4e. </bod">
Ka.a code(
public class Dome#age extends Web#age public Dome#age7%inal #age#arameters parameters: super7parameters:1 %inal ClickCounter/abel clickCounter/abel 8 neC ClickCounter/abel79clickCounter/abel9, 9Click on meF9:1 %inal /abel clickCounter 8 neC /abel79clickCounter9, neC #ropert";odel7clickCounter/abel, 9clickCounter9::1
clickCounter/abel.set'utput;arkup$d7true:1 clickCounter/abel.add7neC 52ax(4ent.eha4ior79click9: !'4erride protected 4oid on(4ent752ax*eEuestTarget target: clickCounter/abel.clickCounter>>1 target.add7clickCounter:1 & &:1 add7clickCounter/abel:1 add7clickCounter.set'utput;arkup$d7true::1 & & class ClickCounter/abel extends /abel public int clickCounter1 public ClickCounter/abel7)tring id: super7id:1 & public ClickCounter/abel7)tring id, $;odel<?> model: super7id, model:1 & public ClickCounter/abel7)tring id, )tring label: super7id, label:1
1AA
& &
-n the code abo.e we ha.e declared a custom label class named ClickCo"nterLabel that e,poses a public integer field called clickCounter. Then* in the home page we ha.e attached a A5a('!ent1eha!ior to our custom label to increment clickCounter e.ery time it recei.es a clic# e.ent. The number of clic#s is displayed with another standard label named clickCounter.
1-.3.2
A<axHorm8u=mit;ehavior
This beha.ior allows to send a form .ia AKAL when the component it is attached to recei.es the specified e.ent. The component doesnBt need to be inside the form if we use the constructor .ersion that* in addition to the name of the e.ent* ta#es in input also the target form(
3orm %orm 8 neC 3orm79%orm9:1 .utton submit.utton 8 neC .utton79submit.utton9:1 //submit %orm Chen button is clicked submit.utton.add7neC 52ax3orm)ubmit.eha4ior7%orm, 9click9: &:1 add7%orm:1 add7submit.utton:1
1-.3.3
A<axHorm.omponent0pdating;ehavior
This beha.ior updates the model of the form component it is attached to when a gi.en e.ent occurs. The standard form submitting process is s#ipped and the beha.ior .alidates only its form component. The beha.ior doesnBt wor# with radio buttons and chec#bo,es. =or these #inds of components we must use A5a(FormChoiceComponent9pdating1eha!ior(
3orm %orm 8 neC 3orm79%orm9:1 Text3ield text3ield 8 neC Text3ield79text3ield9, ;odel.o%799::1 //update the model o% the text %ield each time e4ent 9change9 occurs text3ield.add7neC 52ax3ormComponent0pdating.eha4ior79change9: !'4erride protected 4oid on0pdate752ax*eEuestTarget target: //... & &:1 add7%orm.add7text3ield::1
1-.3.#
A=stractA<ax*imer;ehavior
AbstractA5a(/imer1eha!ior e,ecutes callbac# method on/imer(A5a()e6"est/arget target at a specified inter.al. The beha.ior can be stopped and restarted at a later time with methods stop(A5a()e6"est/arget target and restart(A5a()e6"est/arget target (
/abel d"namic/abel 8 neC /abel79d"namic/abel9:1 //trigger an 5L5] reEuest e4er" three seconds d"namic/abel.add7neC 5bstract52axTimer.eha4ior7+uration.seconds7R:: !'4erride protected 4oid onTimer752ax*eEuestTarget target: //... & &:1
1A6
add7d"namic/abel:1
-f we need to change the default picture used as acti.ity indicator* we can o.erride method get2ndicator9rl( of A5a(2ndicatorAppender and return the &+: to the desired picture.
1A7
5escription The callbac# &+: used to ser.e the AKAL reEuest that will be sent. The id of the component that wants to start the AKAL call. A list of e.ent (clic#* change* etc...) that can trigger the AKAL call. The reEuest method that must be used (0IT or P@"T). The id of the form that must be submitted with the AKAL call. -f the AKAL call in.ol.es the submission of a form* this flag indicates whether the data must be encoded using the encoding mode Fmultipart/form-dataG. The input name of the submitting component of the form A boolean parameter that indicates if the AKAL call is asynchronous (true ) or not. "pecifies the type of data returned by the AKAL call (L<:* 9T<:* K"@ * etc...).
5efault value
domready 0IT
%alse
true
L<:
This is a list of the listeners that are e,ecuted on client side (they are bh* pre* bsh* Ka.a"cript scripts) during the lifecycle of an AKAL reEuest. Iach ah* sh* fh* short name is the abbre.iation of one of the methods defined in the coh interface 2A5a(CallListener (see below). >ote
An empty list
A full list of the a.ailable reEuest parameters as well as more details on the related Ka.a"cript code can be found at https(//cwi#i.apache.org/confluence/ display/2-!MIT/2ic#etNAHa,. Parameters BuB (callbac# &+:) and BcB (the id of the component) are generated by the AKAL beha.ior that will ser.e the AKAL call and they are not accessible through A5a()e6"estAttrib"tes. 9ere is the final AKAL function generate for the beha.ior used in e,ample proHect $-a0#vent9ehavior #0ample:
Wicket.52ax.a2ax7 9u9:9./?A-@.$.eha4ior/istener.A-clickCounter/abel9,9e9:9click9, 9c9:9clickCounter/abel@9&:1
I.en if most of the times we will let 2ic#et generate reEuest attributes for us* both AKAL components and beha.iors gi.e us the chance to modify them o.erriding their method "pdateA5a(Attrib"tes (A5a()e6"estAttrib"tes attrib"tes . @ne of the attribute we may need to modify is the list of 2A5a(CallListeners returned by method getA5a(CallListeners( . 2A5a(CallListener defines a set of methods which return the Ka.a"cript code (as a CharSe6"ence) that must be e,ecuted on client side when the AKAL reEuest handling reaches a gi.en stage( get;eforeHandlerJ.omponent@( returns the Ka.a"cript code that will be e,ecuted before any other handlers returned by 2A5a(CallListener. The code is e,ecuted in a scope where it can use .ariable attrs* which is an array containing
Wicket free user guide
1A8
the K"@ parameters passed to Wicket.A5a(.a5a(. getPreconditionJ.omponent@( returns the Ka.a"cript code that will be used as precondition for the AKAL call. -f the script returns false then neither the AHa, call nor the other handlers will be e,ecuted. The code is e,ecuted in a scope where it can use .ariable attrs* which is the same .ariable seen for get1e0oreHandler. get;efore8endHandlerJ.omponent@( returns the Ka.a"cript code that will be e,ecuted Hust before the AKAL call is performed. The code is e,ecuted in a scope where it can use .ariables attrs* 2E]D* and settings( - attrs is the same .ariable seen for get1e0oreHandler. - 2E]D* is the the HQuery L<:9ttp+eEuest obHect used to ma#e the AKAL call. - settings contains the settings used for calling HQuery.aHa,(). getAfterHandlerJ.omponent@( returns the Ka.a"cript code that will be e,ecuted after the AKAL call. The code is e,ecuted in a scope where it can use .ariable attrs* which is the same .ariable seen before for get1e0oreHandler. get8uccessHandlerJ.omponent@( returns the Ka.a"cript code that will be e,ecuted if the AKAL call has successfully returned. The code is e,ecuted in a scope where it can use .ariables attrs* 2E]D** data and text)tatus( - attrs and 2E]D* are same .ariables seen for get1e0oreSendHandler( - data is the data returned by the AKAL call. -ts type depends on parameter Cr (2ic#et AKAL response). - text)tatus itBs the status returned as te,t. getHailureHandlerJ.omponent@( returns the Ka.a"cript code that will be e,ecuted if the AKAL call has returned with a failure. The code is e,ecuted in a scope where it can use .ariable attrs* which is the same .ariable seen for get1e0oreHandler. get.ompleteHandlerJ.omponent@( returns the Ka.a"cript that will be in.o#ed after success or failure handler has been e,ecuted. The code is e,ecuted in a scope where it can use .ariables attrs* 2E]D* and text)tatus which are the same .ariables seen for getS"ccessHandler.
-n the ne,t paragraph we will see an e,ample of custom 2A5a(CallListener designed to disable a component during AKAL reEuest processing.
1-.-.1
The listener should e,ecute some Ka.a"cript code to disable a gi.en component when the component it is attached to is about to ma#e an AKAL call. Then* when the AKAL reEuest has been completed* the listener should bring bac# the disabled component to an acti.e state. 2hen a component is disabled it must be clear to user that an AKAL reEuest is running and that he/she must wait for it to complete. To achie.e this result we want to disable a gi.en component co.ering it with a semi-transparent o.erlay area with an acti.ity indicator in the middle. The final result will loo# li#e this(
Wicket free user guide
16%
1-.-.2
The listener will implement methods get1e0oreHandler and getA0terHandler( the first will return the code needed to place an o.erlay <di4> on the desired component while the second must remo.e this o.erlay when the AKAL call has completed. To mo.e and resiCe the o.erlay area we will use another module from KQuery&- library that allows us to position D@< elements on our page relati.e to another element 5A. "o our listener will depend on four static resources( the KQuery library* the position module of KQuery &-* the custom code used to mo.e the o.erlay <di4> and the picture used as acti.ity indicator. I,cept for the acti.ity indicator* all these resources must be added to page header section in order to be used. AHa, call listeners can contribute to header section by simply implementing interface 2ComponentAwareHeaderContrib"tor. 2ic#et pro.ides adapter class A5a(CallListener that implements both 2A5a(CallListener and 2ComponentAwareHeaderContrib"tor. 2e will use this class as base class for our listener.
1-.-.3
7ava8cript code
ow that we #now what to do on the Ka.a side* letBs ha.e a loo# at the custom Ka.a"cript code that must be returned by our listener (file mo.e9iderAnd-ndicator.Hs)(
+isableComponent/istener 8 disable(lement: %unction7element$d, acti4e$con0rl: 4ar hider$d 8 element$d > 9-disable-la"er91 4ar indicator$d 8 element$d > 9-indicator-picture91 element$d 8 9X9 > element$d1 //create the o4erla" <di4> W7element$d:.a%ter7I<di4 id89I > hider$d > I9 st"le89position:absolute19>I > I<img id89I > indicator$d > > I</di4>I:1 hider$d 8 9X9 > hider$d1 //set the st"le properties o% the o4erla" <di4> W7hider$d:.css7Iopacit"I, IA.BI:1 W7hider$d:.css7Itext-alignI, IcenterI:1 W7hider$d:.css7Ibackground-colorI, IWhite)mokeI:1 W7hider$d:.css7IborderI, I@px solid +ark-ra"I:1 //set the dimention o% the o4erla" <di4> W7hider$d:.Cidth7W7element$d:.outerWidth7::1 W7hider$d:.height7W7element$d:.outerDeight7::1
56 http: -5ueryui.com position
161
//positioning the o4erla" <di4> on the component that must be disabled. W7hider$d:.position7 o%: W7element$d:,at: Itop le%tI, m": Itop le%tI&:1 //positioning the acti4it" indicator in the middle o% the o4erla" <di4> W79X9 > indicator$d:.position7 o%: W7hider$d:, at: Icenter centerI, m": Icenter centerI&:1 &, //%unction hideComponent
=unction +isableComponent/istener.disable(lement places the o.erlay <di4> an the acti.ity indicator on the desired component. The parameters in input are the mar#up id of the component we want to disable and the &+: of the acti.ity indicator picture. These two parameters must be pro.ided by our custom listener. The rest of custom Ka.a"cript contains function +isableComponent/istener.hideComponent which is Hust a wrapper around the KQuery function remo4e7::
hideComponent: %unction7element$d: 4ar hider$d 8 element$d > 9-disable-la"er91 W7IXI > hider$d:.remo4e7:1 & &1
1-.-.#
.lass code
164
protected Char)eEuence get$ndicator0rl7Component component: return component.url3or7indicator*e%erence, null:1 & !'4erride public 4oid renderDead7Component component, $Deader*esponse response: *esource*e%erence 2Euer"*e%erence 8 5pplication.get7:.getLa4a)cript/ibrar")ettings7:. getLVuer"*e%erence7:1 response.render7La4a)criptDeader$tem.%or*e%erence72Euer"*e%erence::1 response.render7La4a)criptDeader$tem.%or*e%erence72Euer"0i#osition*e%::1 response.render7La4a)criptDeader$tem.%or*e%erence7custom)cript*e%erence::1 & &
As you can see in the code abo.e we ha.e created a function ( get2ndicator9rl) to retrie.e the &+: of the indicator picture. This was done in order to ma#e the picture customiCable by o.erriding this method. @nce we ha.e our listener in place* we can finally use it in our e,ample o.erwriting method "pdateA5a(Attrib"tes of the AKAL button that submits the form(
//... neC 52ax.utton79a2ax.utton9: !'4erride protected 4oid update52ax5ttributes752ax*eEuest5ttributes attributes: super.update52ax5ttributes7attributes:1 attributes.get52axCall/isteners7:.add7neC +isableComponent/istener7%orm::1 & & //...
1-.-.(
Glo=al listeners
"o far we ha.e seen how to use an AKAL call listener to trac# the AKAL acti.ity of a single component. -n addition to these #inds of listeners* 2ic#et pro.ides also global listeners which are triggered for any AKAL reEuest sent from a page. 0lobal AKAL call e.ents are handled with Ka.a"cript. 2e can register a callbac# function for a specific e.ent of the AKAL call lifecycle with function Wicket.(4ent.subscribe7I<e4entName>I, <callback 3unction>:. The first parameter of this function is the name of the e.ent we want to handle. The possible names are( I/a2ax/call/be%oreI( called before any other e.ent handler. I/a2ax/call/be%ore)endI( called Hust before the AKAL call. I/a2ax/call/a%terI( called after the AKAL reEuest has been sent. I/a2ax/call/successI( called if the AKAL call has successfully returned. I/a2ax/call/%ailureI( called if the AKAL call has returned with a failure. I/a2ax/call/completeI( called when the AKAL call has completed. I/dom/node/remo4ingI( called when a component is about to be remo.ed .ia AKAL. This happens when component mar#up is updated .ia AKAL (i.e. the component itself or one of its containers has been added to A5a()e6"est/arget) I/dom/node/addedI( called when a component has been added .ia AKAL. Kust li#e I/dom/node/remo4ingI* this e.ent is triggered when a component is added to A5a()e6"est
16$
/arget. The callbac# function ta#es in input the following parameters( attrs* 2E]D** text)tatus* 2E(4ent and The first three parameters are the same seen before with 2A5a(CallListener while 2E(4ent is an e.ent internally fired by 2ic#et. The last parameter errorThroCn indicates if an error has occurred during the AKAL call. To see a basic e,ample of use of a global AKAL call listener* letBs go bac# to our custom datepic#er created in chapter 13. 2hen we built it we didnBt thin# about a possible use of the component with AKAL. 2hen a comple, component li#e our datepic#er is refreshed .ia AKAL* the following two side effects can occur( After been refreshed* the component loses e.ery Ka.a"cript handler set on it. This is not a problem for our datepic#er as it sets a new KQuery datepic#er e.ery time is rendered (inside method renderHead). The mar#up pre.iously created with Ka.a"cript is not remo.ed. =or our datepic#er this means that the icon used to open the calendar wonBt be remo.ed while a new one will be added each time the component is refreshed.
errorThroCn.
To sol.e the second unwanted side effect we can register a global AKAL call listener that completely remo.es the datepic#er functionality from our component before it is remo.ed due to an AKAL refresh (which fires e.ent I/dom/node/remo4ingI). ProHect !ustomDatepic#erAHa, contains a new .ersion of our datepic#er which adds to its Ka.a"cript file KQDatePic#er.Hs the code needed to register a callbac# function that gets rid of the KQuery datepic#er before the component is remo.ed from the D@<(
Wicket.(4ent.subscribe7I/dom/node/remo4ingI, %unction72E(4ent, attributes, 2E]D*, errorThroCn, text)tatus: 4ar component$d 8 IXI > attributesHIidIJ1 i%7W7component$d:.datepicker F88 unde%ined: W7component$d:.datepicker7Idestro"I:1 & :1
The code abo.e retrie.es the id of the component that is about to be remo.ed using parameter attributes. Then it chec#s if a KQuery datepic#er was defined for the gi.en component and if so* it remo.es the widget calling function destro".
1-.' 8ummar
AKAL is another e,ample of how 2ic#et can simplify web technologies pro.iding a good component and obHect oriented abstraction of them. -n this chapter we ha.e seen how to ta#e ad.antage of the AKAL support pro.ided by 2ic#et to write AKAL-enhanced applications. <ost of the chapter has been dedicated to the built-in components and beha.iors that let us adopt AKAL without almost any effort. -n the final part of the chapter we ha.e seen how 2ic#et physically implements an AKAL call on client side using AKAL reEuest attributes. Then* we ha.e learnt how to use call listeners to e,ecute custom Ka.a"cript during AKAL reEuest lifecycle.
163
57 58 59 60 61 62 63
http: www.springsource.org http: en.wikipedia.org wiki #nterprise13ava9eans http: en.wikipedia.org wiki @ependency1In-ection https: github.com wicketstuff 7n overview of Wicket'tuff pro#ect is available in 7ppendix B 'eam has its own integration module for Wicket6 but it is stuck on Wicket version 1$3 http: code.google.com p google*guice http: opene-b.apache.org
165
-n this e,ample the obHect that we want to inHect is a simple class containing a greeting message(
!;anaged.ean public class (nterprise;essage public )tring message 8 9Welcome to the (L. CorldF91 &
Please note that we ha.e used annotation !;anaged.ean to decorate our obHect. ow to inHect it into the home page we must add a field of type 'nterpriseMessage and annotate it with annotation !(L.(
public class Dome#age extends Web#age !(L. pri4ate (nterprise;essage enterprise;essage1 //getter and setter %or enterprise;essage... public Dome#age7%inal #age#arameters parameters: super7parameters:1 add7neC /abel79message9, enterprise;essage.message::1 & &
That is all. 2e can point the browser to the home page of the proHect and see the greeting message inHected into the page(
16A
public 4oid init7: super.init7:1 5nnotationCon%ig5pplicationContext ctx 8 neC 5nnotationCon%ig5pplicationContext7:1 //)can package %or annotated beans ctx.scan79org.CicketTutorial.e2b.ean9:1 ctx.re%resh7:1 getComponent$nstantiation/isteners7:.add7neC )pringComponent$n2ector7this, ctx::1 & &
As we can see in the code abo.e* the constructor of SpringComponent2n5ector ta#es in input also an instance of "pring conte,t. The inHected obHect is the same used in the pre.ious proHect #-bIn-ection#0ample* it differs only for the greeting message(
!;anaged.ean public class (nterprise;essage public )tring message 8 9Welcome to the )pring CorldF91 &
-n the home page of the proHect the obHect is inHected using 2ic#et annotation !)pring.ean(
public class Dome#age extends Web#age !)pring.ean pri4ate (nterprise;essage enterprise;essage1 //getter and setter %or enterprise;essage... public Dome#age7%inal #age#arameters parameters: super7parameters:1 add7neC /abel79message9, enterprise;essage.message::1 & &
By default !)pring.ean searches into "pring conte,t for a bean ha.ing the same type of the annotated field. -f we want we can specify also the name of the bean to use as inHected obHect and we can declare if the dependency is reEuired or not. By default dependencies are reEuired and if they can not be resol.ed to a compatible bean* 2ic#et will throw an 2llegalState'(ception(
//set the dependenc" as not reEuired, i.e the %ield can be le%t null !)pring.ean7name89anotherName9, reEuired8%alse: pri4ate (nterprise;essage enterprise;essage1
166
//in2ect a bean speci%"ing its name Cith L)*-RRA annotations !$n2ect !Named79anotherName9: pri4ate (nterprise;essage enterprise;essage1
1'.# 8ummar
-n this chapter we ha.e seen how to integrate 2ic#et applications with "pring and with an IKB container. <odule Cicket-examples contains also an e,ample of integration with 0uice (see application class org.apache.wicket.e(amples.g"ice.*"iceApplication).
167
12.1 Authentication
The first step in implementing a security policy is assigning a trusted identity to our users* which means that we must authenticate them. 2eb applications usually adopt a form-based authentication with a login form that as#s user for a uniEue username and the relati.e password(
llustration 1=$1% 0ogin form from Wikipedia 2ic#et supports form-based authentication with session class A"thenticatedWebSession and application class A"thenticatedWebApplication* both placed inside pac#age org.apache. wicket.a"throles.a"thentication.
12.1.1
AuthenticatedWe=8ession
!lass A"thenticatedWebSession comes with the following set of public methods to manage user authentication( authenticateJ8tring usernameG 8tring password@ ( this is an abstract method that must be implemented by e.ery subclass of A"thenticatedWebSession. -t should contain the actual code that chec#s for userBs identity. -t returns a boolean .alue which is tr"e if authentication has succeeded or 0alse otherwise. sign!nJ8tring usernameG 8tring password@( this method internally calls a"thenticate and set the flag signed$n to tr"e if authentication succeeds. is8igned!nJ@(getter method for flag signed$n. signCutJ@( sets the flag signed$n to 0alse. invalidateJ@( calls sign+"t and in.alidates session. Warning +emember that sign+"t does not discard any session-relati.e data. -f we want to get rid of these data* we must in.o#e method in!alidate instead of sign+"t.
168
Another abstract method we must implement when we use A"thenticatedWebSession is get)oles which is inherited from parent class AbstractA"thenticatedWebSession. This method can be ignored for now as it will be discussed later when we will tal# about role-based authoriCation.
12.1.2
AuthenticatedWe=Application
!lass A"thenticatedWebApplication pro.ides the following methods to support form-based authentication( getWe=8ession.lassJ@( abstract method that returns the session class to use for this application. The returned class must be a subclass of AbstractA"thenticatedWeb Session. get8ign!nPage.lassJ@( abstract method that returns the page to use as sign in page when a user must be authenticated. restart3esponseAt8ign!nPageJ@( forces the current response to restart at the sign in page. After we ha.e used this method to redirect a user* we can ma#e her/him return to the original page calling ComponetBs method contin"e/o+riginal:estination( . The other methods implemented inside A"thenticatedWebApplication will be introduced when we will tal# about authoriCations.
12.1.3
ProHect 9asic$uthentication#0ample is a basic e,ample of form-based authentication implemented with classes A"thenticatedWebSession and A"thenticatedWebApplication. The homepage of the proHect contains only a lin# to page A"thenticatedPage which can be accessed only if user is signed in. The code of A"thenticatedPage is this following(
public class 5uthenticated#age extends Web#age !'4erride protected 4oid onCon%igure7: 5uthenticatedWeb5pplication app 8 75uthenticatedWeb5pplication:5pplication.get7:1 //i% user is not signed in, redirect him to sign in page i%7F5uthenticatedWeb)ession.get7:.is)igned$n7:: app.restart*esponse5t)ign$n#age7:1 & !'4erride protected 4oid on$nitialiPe7: super.on$nitialiPe7:1 add7neC /ink79goToDome#age9: !'4erride public 4oid onClick7: set*esponse#age7get5pplication7:.getDome#age7::1 & &:1 add7neC /ink79log'ut9: !'4erride public 4oid onClick7: 5uthenticatedWeb)ession.get7:.in4alidate7:1 set*esponse#age7get5pplication7:.getDome#age7::1 &
17%
Page A"thenticatedPage chec#s inside onCon0ig"re if user is signed in and if not* it redirects her/him to the sign in page with method restart)esponseAtSign2nPage. The page contains also a lin# to the homepage and another lin# that signs out user. The sign in page is implemented in class Sign2nPage and contains the form used to authenticate users(
public class )ign$n#age extends Web#age pri4ate )tring username1 pri4ate )tring passCord1 !'4erride protected 4oid on$nitialiPe7: super.on$nitialiPe7:1 )tateless3orm %orm 8 neC )tateless3orm79%orm9: !'4erride protected 4oid on)ubmit7: i%7)trings.is(mpt"7username:: return1 boolean auth*esult 8 5uthenticatedWeb)ession.get7:.sign$n7username, passCord:1 //i% authentication succeeds redirect user to the reEuested page i%7auth*esult: continueTo'riginal+estination7:1 & &1 %orm.set+e%ault;odel7neC Compound#ropert";odel7this::1 %orm.add7neC Text3ield79username9::1 %orm.add7neC #assCordText3ield79passCord9::1 add7%orm:1 & &
The form is responsible for handling user authentication inside its method onS"bmit. The username and password are passed to A"thenticatedWebSessionBs method sign2n("sername4 password and if authentication succeeds* the user is redirected to the original page with method contin"e/o+riginal:estination. The session class and the application class used in the proHect are reported here( "ession class(
public class .asic5uthentication)ession extends 5uthenticatedWeb)ession public .asic5uthentication)ession7*eEuest reEuest: super7reEuest:1 &
171
!'4erride public boolean authenticate7)tring username, )tring passCord: //user is authenticated i% both username and passCord are eEual to ICicketerI return username.eEuals7passCord: UU username.eEuals79Cicketer9:1 & !'4erride public *oles get*oles7: return null1 & &
Application class(
public class Wicket5pplication extends 5uthenticatedWeb5pplication !'4erride public Class<Dome#age> getDome#age7: return Dome#age.class1 & !'4erride protected Class<? extends 5bstract5uthenticatedWeb)ession> getWeb)essionClass7: return .asic5uthentication)ession.class1 & !'4erride protected Class<? extends Web#age> get)ign$n#ageClass7: return )ign$n#age.class1 & &
The authentication logic inside a"thenticate has been #ept Euite tri.ial in order to ma#e the code as clean as possible. Please note also that session class must ha.e a constructor that accepts an instance of class )e6"est.
12.1.#
<ethod restart)esponseAtSign2nPage is an e,ample of redirecting user to an intermediate page before allowing him to access to the reEuested page. This method internally throws e,ception org.apache.wicket.)estart)esponseAt2nterceptPage'(ception which sa.es the &+: of the reEuested page into session metadata and then redirects user to the page passed as constructor parameter (the sign in page). ComponentBs method redirect/o2nterceptPage(Page wor#s in much the same way as restart)esponseAtSign2nPage but it allows us to specify which page to use as intermediate page(
redirectTo$ntercept#age7intermediate#age:1
>ote "ince both restart)esponseAtSign2nPage and redirect/o2ntercept Page internally throw an e,ception* the code placed after them will not be e,ecuted.
174
12.2 Authori6ations
The authoriCation support pro.ided by 2ic#et is built around the concept of authori<ation strategy which is represented by interface 2A"thori3ationStrateg, (in pac#age org.apache.wicket .a"thori3ation)(
public inter%ace $5uthoriPation)trateg" //inter%ace methods <T extends $*eEuestableComponent> boolean is$nstantiation5uthoriPed7Class<T> componentClass:1 boolean is5ction5uthoriPed7Component component, 5ction action:1 //de%ault authoriPation strateg" that alloCs e4er"thing public static %inal $5uthoriPation)trateg" 5//'WM5// 8 neC $5uthoriPation)trateg"7: !'4erride public <T extends $*eEuestableComponent> boolean is$nstantiation5uthoriPed7%inal Class<T> c: return true1 & !'4erride public boolean is5ction5uthoriPed7Component c, 5ction action: return true1 & &1 &
This interface defines two methods( is2nstantiationA"thori3ed chec#s if user is allowed to instantiate a gi.en component. isActionA"thori3ed chec#s if user is authoriCed to perform a gi.en action on a componentBs instance. The standard actions chec#ed by this method are defined into class Action and are Action.'NA1L' and Action.)'N:'). -nside 2A"thori3ationStrateg, we can also find a default implementation of the interface (called 5//'WM5//) that allows e.eryone to instantiate e.ery component and perform e.ery possible action on it. This is the default strategy adopted by class Application. To change the authoriCation strategy in use we must register the desired implementation into security settings (interface 2Sec"rit,Settings) during initialiCation phase with method setA"thori3ation Strateg,(
//5pplication class code... !'4erride public 4oid init7: super.init7:1 get)ecurit")ettings7:.set5uthoriPation)trateg"7m"5uthoriPation)trateg":1 & //...
-f we want to combine the action of two or more authoriCation strategies we can chain them with
Wicket free user guide
17$
strategy Compo"ndA"thori3ationStrateg, which implements composite patter for authoriCation strategiesA5. <ost of the times we wonBt need to implement an 2A"thori3ationStrateg, from scratch as 2ic#et already comes with a set of built-in strategies. -n the ne,t paragraphs we will see some of these strategies that can be used to implement an effecti.e and fle,ible security policy.
12.2.1
8implePageAuthori6ation8trateg
Abstract class SimplePageA"thori3ationStrateg, (in pac#age org.apache.wicket. a"thori3ation.strategies.page) is a strategy that chec#s user authoriCations calling abstract method isA"thori3ed only for those pages that are subclasses of a gi.en supertype. -f isA"thori3ed returns %alse * the user is redirected to the sign in page specified as second constructor parameter(
)imple#age5uthoriPation)trateg" authoriPation)trateg" 8 neC )imple#age5uthoriPation)trateg"7 #ageClassToCheck.class, )ign$n#age.class: protected boolean is5uthoriPed7: //5uthentication code... & &1
By default SimplePageA"thori3ationStrateg, chec#s for permissions only on pages. -f we want to change this beha.ior and chec# also other #inds of components* we must o.erride method isActionA"thori3ed and implement our custom logic inside it.
12.2.2
3ole?=ased strategies
At the end of paragraph 17.1.1 we ha.e introduced AbstractA"thenticatedWebSessionBs method get)oles which is pro.ided to support role-based authoriCation returning the set of roles granted to the current user. -n 2ic#et roles are simple strings li#e FBA"-!O&"I+G or FAD<- G (they donBt need to be capitaliCed) and they are handled with class org.apache.wicket.a"throles.a"thori3ation.strategies. role.)oles. This class e,tends standard HashSet collection adding some functionalities to chec# whether the set contains one or more roles. !lass )oles already defines roles )oles.9S') and )oles.A:M2N. The session class in the following e,ample returns a custom F"-0 IDO- G role for e.ery authenticated user and it adds an )oles.A:M2N role if username is eEual to superuser(
class .asic5uthentication*oles)ession extends 5uthenticatedWeb)ession pri4ate )tring userName1 public .asic5uthentication*oles)ession7*eEuest reEuest: super7reEuest:1 & !'4erride public boolean authenticate7)tring username, )tring passCord: boolean auth*esult8 %alse1 auth*esult 8 //some authentication logic...
65 "n page 3> we have seen the same pattern implemented by Ae&uestCycle0istenerCollection for re&uest cycle listeners $
173
i%7auth*esult: userName 8 username1 return auth*esult1 & !'4erride public *oles get*oles7: *oles result*oles 8 neC *oles7:1 i%7is)igned$n7:: result*oles.add79)$-N(+M$N9:1 i%7userName.eEuals79superuser9:: result*oles.add7*oles.5+;$N:1 return result*oles1 & &
+oles can be adopted to apply security restrictions on our pages and components. This can be done using one of the two built-in authoriCation strategies that e,tend super class Abstract)ole A"thori3ationStrateg,Wicket( Meta:ata)oleA"thori3ationStrateg, and Annotations )oleA"thori3ationStrateg, The difference between these two strategies is that Meta:ata)oleA"thori3ationStrateg, handles role-based authoriCations with 2ic#et metadata while Annotations)oleA"thori3ation@ Strateg, uses Ka.a annotations. >ote Application class A"thenticatedWebApplication already sets Meta:ata )oleA"thori3ationStrateg, and Annotations)oleA"thori3ation Strateg, as its own authoriCation strategies (it uses a compound strategy as we will see in paragraph 17.4.3). The code that we will see in the ne,t e,amples is for illustrati.e purpose only. -f our application class inherits from A"thenticatedWebApplication we wonBt need to configure anything to use these two strategies. 12.2.2.1 0sing roles with metadata "trategy Meta:ata)oleA"thori3ationStrateg, uses application and components metadata to implement role-based authoriCations. The class defines a set of static methods a"thori3e that can be used to specify which roles are allowed to instantiate a component and which roles can perform a gi.en action on a component. The following code snippet reports both application and session classes from proHect 4eta@ata"olesStrategy#0ample and illustrates how to use Meta:ata)oleA"thori3ationStrateg, to allow access to a gi.en page (Admin+nl,Page) only to AD<- role( Application class(
public class Wicket5pplication extends 5uthenticatedWeb5pplication !'4erride public Class<? extends Web#age> getDome#age7:
175
return Dome#age.class1 & !'4erride protected Class<? extends 5bstract5uthenticatedWeb)ession> getWeb)essionClass7: return .asic5uthentication)ession.class1 & !'4erride protected Class<? extends Web#age> get)ign$n#ageClass7: return )ign$n#age.class1 & !'4erride public 4oid init7: get)ecurit")ettings7:.set5uthoriPation)trateg"7neC ;eta+ata*ole5uthoriPation)trateg" 7this::1 ;eta+ata*ole5uthoriPation)trateg".authoriPe75dmin'nl"#age.class, *oles.5+;$N:1 & &
"ession class(
public class .asic5uthentication)ession extends 5uthenticatedWeb)ession pri4ate )tring username1 public .asic5uthentication)ession7*eEuest reEuest: super7reEuest:1 & !'4erride public boolean authenticate7)tring username, )tring passCord: //user is authenticated i% username and passCord are eEual boolean auth*esult 8 username.eEuals7passCord:1 i%7auth*esult: this.username 8 username1 return auth*esult1 & public *oles get*oles7: *oles result*oles 8 neC *oles7:1 //i% user is signed in add the relati4e role i%7is)igned$n7:: result*oles.add79)$-N(+M$N9:1 //i% username is eEual to IsuperuserI add the 5+;$N role i%7usernameF8 null UU username.eEuals79superuser9:: result*oles.add7*oles.5+;$N:1 return result*oles1 & !'4erride
17A
The code that instantiates Meta:ata)oleA"thori3ationStrateg, and set it as applicationBs strategy is inside application class method init. Any subclass of Abstract)oleA"thori3ationStrateg,Wicket needs an implementation of interface 2)oleCheckingStrateg, to be instantiated. =or this purpose in the code abo.e we used the application class itself because its base class A"thenticatedWebApplication already implements interface 2)oleCheckingStrateg,. By default A"thenticatedWebApplication chec#s for authoriCations using the roles returned by the current AbstractA"thenticated WebSession. As final step inside init we grant the access to page Admin+nl,Page to AD<- role calling method a"thori3e. The code from session class has three interesting methods. The first is a"thenticate which considers as .alid credentials e.ery pair of username and password ha.ing the same .alue. The second notable method is get)oles which returns role "-0 IDO- if user is authenticated and it adds role AD<- if username is eEual to superuser. =inally* we ha.e method sign+"t which has been o.erridden in order to clean the username field used internally to generate roles. ow if we run the proHect and we try to access to Admin+nl,Page from the home page without ha.ing the AD<- role* we will be redirected to the default access-denied page used by 2ic#et(
The access-denied page can be customiCed using method setAccess:eniedPage(Class7D e(tends Page8 of setting interface 2ApplicationSettings(
//5pplication class code... !'4erride public 4oid init7: get5pplication)ettings7:.set5ccess+enied#age7;"Custom5ccess+enied#age.class:1 &
Kust li#e custom FPage e,piredG page (see chapter A.4.5)* also custom FAccess deniedG page must be boo#mar#able. 12.2.2.2 0sing roles with annotations "trategy Annotations)oleA"thori3ationStrateg, relies on two built-in annotations to handle role-based authoriCations. These annotations are A"thori3e2nstantiation and A"thori3e Action. As their names suggest the first annotation specifies which roles are allowed to instantiate the annotated component while the second must be used to indicate which roles are allowed to perform a specific action on the annotated component. -n the following e,ample we use annotations to ma#e a page accessible only to signed-in users and to enable it only if user has the AD<- role(
Wicket free user guide
176
!5uthoriPe$nstantiation79)$-N(+M$N9: !5uthoriPe5ction7action 8 9(N5./(9, roles 8 public class ;"#age extends Web#age //#age class code... & 95+;$N9&:
+emember that when a component is not enabled* user can render it but he can neither clic# on its lin#s nor interact with its forms. I,ample proHect $nnotations"olesStrategy#0ample is a re.isited .ersion of 4eta@ata"olesStrategy #0ample where we use Annotations)oleA"thori3ationStrateg, as authoriCation strategy. To ensure that page Admin+nl,Page is accessible only to AD<- role we ha.e used the following annotation(
!5uthoriPe$nstantiation795+;$N9: public class 5dmin'nl"#age extends Web#age //#age class code... &
12.2.3
-nterface 29na"thori3edComponent2nstantiationListener (in pac#age org.apache. wicket.a"thori3ation) is pro.ided to gi.e the chance to handle the case in which a user tries to instantiate a component without ha.ing the permissions to do it. The method defined inside this interface is on9na"thori3ed2nstantiation(Component and it is e,ecuted whene.er a user attempts to e,ecute an unauthoriCed instantiation. This listener must be registered into applicationBs security settings with method set9na"thori3ed Component2nstantiationListener defined by setting interface 2Sec"rit,Settings. -n the following code snippet we register a listener that redirect user to a warning page if he tries to do a notallowed instantiation(
public class Wicket5pplication extends 5uthenticatedWeb5pplication //5pplication code... !'4erride public 4oid init7: get)ecurit")ettings7:.set0nauthoriPedComponent$nstantiation/istener7 neC $0nauthoriPedComponent$nstantiation/istener7: !'4erride public 4oid on0nauthoriPed$nstantiation7Component component: component.set*esponse#age75uthWarning#age.class:1 & &:1 & &
-n addition to interface 2)oleCheckingStrateg,* class A"thenticatedWebApplication implements also 29na"thori3edComponent2nstantiationListener and registers itself as listener for unauthoriCed instantiations. By default A"thenticatedWebApplication redirects users to sign-in page if they are not signed-in and they try to instantiate a restricted component. @therwise* if users are already signed in but they are
177
12.2.#
8trateg 3oleAuthori6ation8trateg
!lass )oleA"thori3ationStrateg, is a compound strategy that combines both Meta:ata )oleA"thori3ationStrateg, and Annotations)oleA"thori3ationStrateg,. This is the strategy used internally by A"thenticatedWebApplication.
ow we can use annotation )e6"ireHttps to specify which pages must be ser.ed using 9TTP"(
!*eEuireDttps public class Dome#age extends Web#age public Dome#age7%inal #age#arameters parameters: super7parameters:1 & &
-f we want to protect many pages with 9TTP" without adding annotation )e6"ireHttps to each of them* we can annotate a mar#er interface or a base page class and implement/e,tend it in any page we want to ma#e secure( <ar#er interface(
!*eEuireDttps public inter%ace $;arker &
Base class(
178
To modify the set of allowed files formats we can add one or more patterns with method addPattern (String . The rules to write a pattern are the following( patterns start with either a XNX or a X-X. -n the first case the pattern will add one or more file to the set while starting a pattern with a F-G we e,clude all the files matching the gi.en pattern. =or e,ample pattern F-web.,mlG e,cludes all web.,ml files in all directories. wildcard character FZG is supported as placeholder for Cero or more characters. =or e,ample pattern FNZ.mp3G adds all the mp3 files inside all directories. subdirectories are supported as well. =or e,ample pattern FNdocuments/Z.pdfG adds all pdf files under FdocumentsG directory. !haracter FZG can be used with directories to specify a nesting le.el. =or e,ample FNdocuments/Z/Z.pdfG adds all pdf files placed one le.el below FdocumentsG directory. a double wildcard character FZZG indicates Cero or more subdirectories. =or e,ample patter FNdocuments/ZZ/Z.pdfG adds all pdf files placed inside FdocumentsG directory or inside any of its subdirectories. Patterns that allow to access to e.ery file with a gi.en e,tensions (such as FNZ.pdfG) should be always a.oided in fa.our of more restricti.e e,pressions that contain a directory structure(
18%
//5pplication class code... !'4erride public 4oid init7: $#ackage*esource-uard package*esource-uard 8 application.get*esource)ettings7: .get#ackage*esource-uard7:1 i% 7package*esource-uard instanceo% )ecure#ackage*esource-uard: )ecure#ackage*esource-uard guard 8 7)ecure#ackage*esource-uard: package*esource-uard1 //5lloC to access onl" to pd% %iles placed in the QpublicN director". guard.add#attern79>public/*.pd%9:1 & &
12.( 8ummar
-n this chapter we ha.e seen the components and the mechanisms that allow us to implement security policies in our 2ic#et-based applications. 2ic#et comes with an out of the bo, support for both authoriCation and authentication. The central element of authoriCation mechanism is the interface 2A"thori3ationStrateg, which decouples our components from any detail about security strategy. The implementations of this interface must decide if a user is allowed to instantiate a gi.en page or component and if she/he can perform a gi.en action on it. 2ic#et nati.ely supports role-based authoriCations with strategies Meta:ata)oleA"thori3ation Strateg, and Annotations)oleA"thori3ationStrateg,. The difference between these two strategies is that the first offers a programmatic approach for role handling while the second promotes a declarati.e approach using built-in annotations. After ha.ing e,plored how 2ic#et internally implements authentication and authoriCation* in the last part of the chapter we ha.e learnt how to configure our applications to support 9TTP" and how to specify which pages must be ser.ed o.er this protocol. -n the last paragraph we ha.e seen how 2ic#et protects pac#age resources with a guard entity that allows us to decide which pac#age resources can be accessed from users.
181
184
utility class pro.ides a set of methods to render a component* clic# lin#s* chec# if page contains a gi.en component or a feedbac# message* and so on. The basic test case shipped with /estHomePage illustrates how Wicket/ester is typically instantiated (inside method set9p( ). -n order to test our components* Wicket/ester needs to use an instance of WebApplication. &sually* we will use our application class as WebApplication* but we can also decide to build Wicket/ester in.o#ing its no-argument constructor and letting it automatically build a moc# web application (an instance of class org.apache.wicket.mock. MockApplication). The code from /estHomePage introduces two basic methods to test our pages. The first is method startPage that renders a new instance of the gi.en page class and sets it as current rendered page for Wicket/ester. The second method is assert)enderedPage which chec#s if the current rendered page is an instance of the gi.en class. -n this way if /estHomePage succeeds we are sure that page HomePage has been rendered without any problem. The last rendered page can be retrie.ed with method getLast)enderedPage. ThatBs only a taste of what Wicket/ester can do. -n the ne,t paragraphs we will see how it can be used to test e.ery element that composes a 2ic#et page (lin#s* models* beha.iors* etc...).
14.1.1
*esting links
A clic# on a 2ic#et lin# can be simulated with method clickLink which ta#es in input the lin# component or the page-relati.e path to it. To see an e,ample of usage of clickLink* letBs consider again proHect %ife(ycleStages"evisited. As we #now from chapter 5 the home page of the proHect alternately displays two different labels (F=irst labelG and F"econd labelG)* swapping between them each time button 9reload9 is clic#ed. The code from its test case chec#s that label has actually changed after button 9reload9 has been pressed(
//... !Test public 4oid sCitch/abelTest7: //start and render the test page tester.start#age7Dome#age.class:1 //assert rendered page class tester.assert*endered#age7Dome#age.class:1 //assert rendered label tester.assert/abel79label9, 93irst label9:1 //simulate a click on 9reload9 button tester.click/ink79reload9:1 //assert rendered label tester.assert/abel79label9, 9)econd label9:1 & //...
-n the code abo.e we ha.e used clickLink to clic# on the 9reload9 button and force page to be rendered again. -n addition* we ha.e used also method assertLabel that chec#s if a gi.en label contains the e,pected te,t. By default clickLink assumes that AKAL is enabled on client side. To switch AKAL off we can use another .ersion of this method that ta#es in input the path to the lin# component and a boolean flag that indicates if AKAL must be enabled (tr"e) or not (0alse).
//... //simulate a click on a button Cithout 5L5] support tester.click/ink79reload9, %alse:1
18$
//...
14.1.2
Wicket/ester pro.ides also a set of methods to test the states of a component. They are( assertDna=ledJ8tring path@Iassert5isa=ledJ8tring path@( they test if a component is enabled or not. assertEisi=leJ8tring path@Iassert!nvisi=leJ8tring path@( they test component .isibility. assert3e1uired(8tring path@( chec#s if a form component is reEuired. -n the test case from proHect (ustom@atepicker$-a0 we used assert'nabled<assert:isabled to chec# if button 9update9 really disables our datepic#er(
//... !Test public 4oid test+isable+ate#ickerWith.utton7: //start and render the test page tester.start#age7Dome#age.class:1 //assert that datepicker is enabled tester.assert(nabled79%orm:datepicker9:1 //click on update button to disable datepicker tester.click/ink79update9:1 //assert that datepicker is disabled tester.assert+isabled79%orm:datepicker9:1 & //...
14.1.3
<ethod startComponent(Component can be used to test a component in isolation without ha.ing to create a container page for this purpose. The target component is rendered and both its methods on2nitiali3e( and on1e0ore)ender( are e,ecuted. -n the test case from proHect (ustom;orm(omponentPanel we used this method to chec# if our custom form component correctly renders its internal label(
//... !Test public 4oid testCustom#anelContains/abel7: Temperature+egree3ield %ield 8 neC Temperature+egree3ield79%ield9, ;odel.o%7A.AA::1 //0se standard L0nit class 5ssert 5ssert.assertNull7%ield.get79mesurament0nit9::1 tester.startComponent7%ield:1 5ssert.assertNotNull7%ield.get79mesurament0nit9::1 & //...
-f test reEuires a page we can use startComponent2nPage(Component generates a page for our component.
which automatically
14.1.#
Wicket/ester allows us to access to the last response generated during testing with method getLast)esponse. The returned .alue is an instance of class MockHttpSer!let)esponse that pro.ides helper methods to e,tract informations from moc#ed reEuest.
183
-n the test case from proHect (ustom"esource4ounting we e,tract the te,t contained in the last response with method get:oc"ment and we chec# if it is eEual to the +"" feed used for the test(
//... !Test public 4oid test;ounted*esource*esponse7: throCs $'(xception, 3eed(xception tester.start*esource7neC *))#roducer*esource7::1 )tring responseTxt 8 tester.get/ast*esponse7:.get+ocument7:1 //Crite the *)) %eed used in the test into a ."te5rra"'utput)tream ."te5rra"'utput)tream output)tream 8 neC ."te5rra"'utput)tream7:1 Writer Criter 8 neC 'utput)treamWriter7output)tream:1 )"nd3eed'utput output 8 neC )"nd3eed'utput7:1 output.output7*))#roducer*esource.get3eed7:, Criter:1 //the response and the *)) must be eEual 5ssert.assert(Euals7responseTxt, output)tream.to)tring7::1 & //...
To simulate a reEuest to the custom resource we used method start)eso"rce which can be used also with resource references.
14.1.(
*esting 03,s
Wicket/ester can be pointed to an arbitrary &+: with method e(ec"te9rl(String "rl . This can be useful to test mounted pages* resources or reEuest mappers(
//... //the resource Cas mapped at I/%oo/barI tester.execute0rl79./%oo/bar9:1 //...
14.1.-
-f our application uses AKAL to refresh components mar#up* we can test if A5a()e6"est/arget contains a gi.en component with Wicket/esterBs method assertComponent+nA5a()esponse(
//... //test i% 52ax*eEuestTarget contains a component 7using its instance: tester.assertComponent'n52ax*esponse7amount/abel:1 //... //test i% 52ax*eEuestTarget contains a component 7using its path: tester.assertComponent'n52ax*esponse79pathTo/abel:label$d9:1
-tBs also possible to use method isComponent+nA5a()esponse(Component cmp component has been added to A5a()e6"est/arget(
//... //test i% 52ax*eEuestTarget does N'T contain amount/abel
to #now if a
assert3alse7tester.isComponent'n52ax*esponse7amount/abel::1 //...
14.1.'
185
Beha.ior A5a('!ent1eha!ior and its subclasses can be tested simulating AKAL e.ents with Wicket/esterBs method e(ec"teA5a('!ent(Component cmp4 String e!ent . 9ere is the sample code from proHect !est$-a0#vents#0ample( 9ome page code:
public class Dome#age extends Web#age public static )tring $N$TMO5/0( 8 9$nitial 4alue91 public static )tring 'TD(*MO5/0( 8 9'ther 4alue91 public Dome#age7%inal #age#arameters parameters: super7parameters:1 /abel label1 add7label 8 neC /abel79label9, $N$TMO5/0(::1 label.add7neC 52ax(4ent.eha4ior79click9: !'4erride protected 4oid on(4ent752ax*eEuestTarget target: //change labelIs data ob2ect getComponent7:.set+e%ault;odel'b2ect7'TD(*MO5/0(:1 target.add7getComponent7::1 & &:.set'utput;arkup$d7true:1 //... & &
Test method(
!Test public 4oid test52ax.eha4ior7: //start and render the test page tester.start#age7Dome#age.class:1 //test i% label has the initial expected 4alue tester.assert/abel79label9, Dome#age.$N$TMO5/0(:1 //simulate an 5L5] 9click9 e4ent tester.execute52ax(4ent79label9, 9click9:1 //test i% label has changed as expected tester.assert/abel79label9, Dome#age.'TD(*MO5/0(:1 &
14.1.2
To test a generic AKAL beha.ior we can simulate a reEuest to it using Wicket/esterBs method e(ec"te1eha!ior(AbstractA5a(1eha!ior beha!ior (
//... 52ax3ormComponent0pdating.eha4ior a2ax.eha4ior 8 neC 52ax3ormComponent0pdating.eha4ior 79change9: !'4erride protected 4oid on0pdate752ax*eEuestTarget target: //... & &1 component.add7a2ax.eha4ior:1
18A
//... //execute 5L5] beha4ior, i.e. on0pdate Cill be in4oked tester.execute.eha4ior7a2ax.eha4ior::1 //...
14.1.4
-n paragraph 1$.8 we ha.e seen how to configure our application to store resource files into a custom folder placed inside webapp root folder (see proHect (ustom;olderF4arkup#0ample). -n order to write testing code for applications that use this #ind of customiCation* we must tell Wicket/ester which folder to use as webapp root. This is necessary as under test en.ironment we donBt ha.e any web ser.er* hence itBs impossible for Wicket/ester to retrie.e this parameter from ser.let conte,t. 2ebapp root folder can be passed to Wicket/esterBs constructor as further parameter li#e we did in the test case of proHect (ustom;olderF4arkup#0ample(
public class TestDome#age pri4ate WicketTester tester1 !.e%ore public 4oid set0p7: //build the path to Cebapp root %older 3ile cur+irector" 8 neC 3ile7)"stem.get#ropert"79user.dir9::1 3ile CebContext+ir 8 neC 3ile7cur+irector", 9src/main/Cebapp9:1 tester 8 neC WicketTester7neC Wicket5pplication7:, CebContext+ir.get5bsolute#ath7::1 & //test methods... &
Form/ester can simulate form submission with method s"bmit which ta#es in input as optional parameter the submitting component to use instead of the default one(
//... //create a neC %orm tester Cithout %illing its %orm components Cith a blank string 3ormTester %ormTester 8 tester.neC3ormTester79%orm9, %alse:1 //submit %orm Cith de%ault submitter %ormTester.submit7:1 //... //submit %orm using inner component IbuttonI as alternate button
186
%ormTester.submit79button9:1
-f we want to submit a form with an e,ternal lin# component we can use method s"bmitLink (String path4 boolean page)elati!e specifying the path to the lin#. -n the ne,t paragraphs we will see how to use Wicket/ester and Form/ester to interact with a form and with its children components.
14.2.1
The purpose of a 9T<: form is to collect user input. Form/ester comes with the following set of methods that simulate input insertion into formBs fields( setEalueJ8tring pathG 8tring value@ ( inserts the gi.en te,tual .alue into the specified component. -t can be used with components /e(tField and /e(tArea. A .ersion of this method that accepts a component instance instead of its path is also a.ailable. setEalueJ8tring check=ox!dG =oolean value@ ( sets the .alue of a gi.en Check1o( component. setHileJ8tring form.omponent!dG Hile fileG 8tring content* pe@ ( sets a File obHect on a File9ploadField component. selectJ8tring form.omponent!dG int index@( selects an option among a list of possible options owned by a component. -t supports components that are subclasses of AbstractChoice along with )adio*ro"p and Check*ro"p. select+ultipleJ8tring form.omponent!dG intMN indexes@( selects all the options corresponding to the gi.en array of inde,es. -t can be used with multiple-choice components li#e Check*ro"p or ListM"ltipleChoice. set-al"e is used inside method insert9sernamePassword to set the username and password fields of the form used in proHect Stateless%ogin;orm(
protected 4oid insert0sername#assCord7)tring username, )tring passCord: //start and render the test page tester.start#age7Dome#age.class:1 3ormTester %ormTester 8 tester.neC3ormTester79%orm9:1 //set credentials %ormTester.setOalue79username9, username:1 %ormTester.setOalue79passCord9, passCord:1 //submit %orm %ormTester.submit7:1 &
14.2.2
To chec# if a page contains one or more e,pected feedbac# messages we can use the following methods pro.ided by Wicket/ester( assertHeed=ackJ8tring pathG 8tring... messages@( asserts that a gi.en panel contains the specified messages assert!nfo+essagesJ8tring... expected!nfo+essages@( asserts that the e,pected info messages are rendered in the page. assertDrror+essagesJ8tring... expectedDrror+essages@( asserts that the e,pected error messages are rendered in the page. assert2n0oMessages and assert'rrorMessages are used in the test case from proHect Stateless%ogin;orm to chec# that form generates a feedbac# message in accordance with the login
187
result(
!Test public 4oid test;essage3or)uccess%ul/ogin7: inser0sername#assCord79user9, 9user9:1 tester.assert$n%o;essages790sername and passCord are correctF9:1 & !Test public 4oid test;essage3or3ailed/ogin 7: inser0sername#assCord79CrongCredential9, 9CrongCredential9:1 tester.assert(rror;essages79Wrong username or passCord9:1 &
14.2.3
*esting models
!omponent model can be tested as well. 2ith method assertModel-al"e we can test if a specific component has the e,pected data obHect inside its model. This method has been used in the test case of proHect 4odel(haining#0ample to chec# if the form and the drop-down menu share the same data obHect(
!Test public 4oid test3orm)elect)ame;odel'b2ect7: #erson/ist+etails person/ist+etails 8 neC #erson/ist+etails7:1 +rop+oCnChoice drop+oCnChoice 8 7+rop+oCnChoice: person/ist+etails.get79persons9:1 /ist choices 8 drop+oCnChoice.getChoices7:1 //select the second option o% the drop-doCn menu drop+oCnChoice.set;odel'b2ect7choices.get7@::1 //start and render the test page tester.start#age7person/ist+etails:1 //assert that %orm has the same data ob2ect used b" drop-doCn menu tester.assert;odelOalue79%orm9, drop+oCnChoice.get;odel'b2ect7::1 &
188
Test method(
!Test public 4oid home#age;arkupTest7: //start and render the test page tester.start#age7Dome#age.class:1 //retrie4e responseIs markup )tring responseTxt 8 tester.get/ast*esponse7:.get+ocument7:1 TagTester tagTester 8 TagTester.createTag."5ttribute7responseTxt, 9class9, 9m"Class9:1 5ssert.assertNotNull7tagTester:1 5ssert.assert(Euals79span9, tagTester.getName7::1 /ist<TagTester> tagTester/ist 8 TagTester.createTags."5ttribute7responseTxt, 9class9, 9m"Class9, %alse:1 5ssert.assert(Euals7=, tagTester/ist.siPe7::1 &
The name of the tag found by /ag/ester can be retrie.ed with its method getName. <ethod create/ags1,Attrib"te returns all the tags that ha.e the gi.en .alue on the gi.en attribute. -n the code abo.e we ha.e used this method to test that our mar#up contains two tags ha.ing attribute class eEual to m"Class.
14.# 8ummar
2ith a component-oriented framewor# we can test our pages and components as we use to do with any other Ka.a entity. 2ic#et offers a complete support for writing testing code* offering built-in tools to test nearly all the elements that build up our applications (pages* containers* lin#s* beha.iors* etc...). The main entity discussed in this chapter has been class Wicket/ester which can be used to write unit tests and acceptance tests for our application* but we ha.e also seen how to test forms with Form/ester and how to inspect mar#up with /ag/ester. -n addition to learning how to use the utility classes pro.ided by 2ic#et for testing* we ha.e also e,perienced the inno.ati.e approach of 2ic#et to web testing that allows to test components in isolation without the need of running our tests with a web ser.er and depending only on K&nit as testing framewor#.
4%%
4%1
As we can read 2ic#et itself discourages us from using DIDI:@P<I T mode into production en.ironment. The running mode of our application can be configured in three different ways. The first one is adding a filter parameter inside deployment descriptor web.,ml(
<%ilter> <%ilter-name>Cicket.;"5pp</%ilter-name> <%ilter-class>org.apache.Cicket.protocol.http.Wicket3ilter</%ilter-class> <init-param> <param-name>applicationClassName</param-name> <param-4alue>org.CicketTutorial.Wicket5pplication</param-4alue> </init-param> <init-param> <param-name>con%iguration</param-name> <param-4alue>deplo"ment</param-4alue> </init-param> </%ilter>
The additional parameter is written in bold. The same parameter can be also e,pressed as conte,t parameter(
<context-param> <param-name>con%iguration</param-name> <param-4alue>deplo"ment</param-4alue> </context-param>
The third way to set the running mode is using system property wicket.con0ig"ration. This parameter can be specified in the command line that starts up the ser.er(
2a4a -+Cicket.con%iguration8deplo"ment ...
+emember that system properties o.erwrite other settings* so they are ideal to ensure that on
4%4
A.2 .reating a Wicket pro<ect from scratch and importing it into our favourite !5D.
>ote -n order to follow the instructions of this paragraph you must ha.e <a.en installed on your system. The installation of <a.en is out of the scope of this guide but you can easily find an e,tensi.e documentation about it on -nternet. Another reEuirement is a good -nternet connection (a flat AD": is enough) because <a.en needs to connect to its central repository to download the reEuired dependencies.
llustration 1% Wicket &uickstart page 9ere we ha.e to specify the root pac#age of our proHect ( .roup"d)* the proHect name (/rti0act"d) and
67 http: maven.apache.org
4%$
which .ersion of 2ic#et we want to use (1ersion). @nce we ha.e run the resulting command in the @" shell* we will ha.e a new folder with the same name of the proHect (i.e the $rtifactId). -nside this folder we can find a file called pom.0ml. This is the main file used by <a.en to manage our proHect. =or e,ample* using Forg.wic#etTutorialG as CroupId and F<yProHectG as $rtifactId* we would obtain the following artifacts(
.\;"#ro2ect _ _ \---src >---main _ _ _ _ _ _ _ _ _ _ _ _ _ _ \---test \---2a4a \---org \---CicketTutorial TestDome#age.2a4a >---2a4a _ _ _ _ _ _ >---resources _ _ \---Cebapp \---W(.-$N3 Ceb.xml log<2.properties \---org \---CicketTutorial Dome#age.html Dome#age.2a4a Wicket5pplication.2a4a pom.xml
Amongst other things* file pom.,ml contains a section delimited by tag <dependencies> which declares the dependencies of our proHect. By default the <a.en archetype will add the following 2ic#et modules as dependencies(
... <dependencies> <F-W$C6(T +(#(N+(NC$() --> <group$d>org.apache.Cicket</group$d> <arti%act$d>Cicket-core</arti%act$d> <4ersion>W Cicket.4ersion&</4ersion> </dependenc"> <dependenc"> <group$d>org.apache.Cicket</group$d> <arti%act$d>Cicket-ioc</arti%act$d> <4ersion>W Cicket.4ersion&</4ersion> </dependenc"> <F-- '#T$'N5/ +(#(N+(NCG <dependenc"> <group$d>org.apache.Cicket</group$d> <arti%act$d>Cicket-extensions</arti%act$d> <4ersion>W Cicket.4ersion&</4ersion> <dependenc">
4%3
-f we need to use more 2ic#et modules or additional libraries* we can add the appropriate L<: fragmentsA7 here.
!5DA -DIA comes with a <a.en importing functionality that can be started under F=ile/ ew ProHect/-mport from e,ternal model/<a.enG. Then* we Hust ha.e to select the pom.,ml file of our proHect(
68 7s described in 7ppendix B6 the ?/0 needed to include a dependency can be found at http: mvnrepository.com
4%5
Dclipse -f our -DI is Iclipse the import procedure is a little more comple,. Before opening the new proHect we must generate the Iclipse proHect artifacts running the following command from proHect root(
m4n eclipse:eclipse
ow to import our proHect into Iclipse we must create a classpath .ariable called 4+1"#P2 that must point to your local <a.en repository. This can be done selecting F2indow/PreferencesG and searching for F!lasspath DariablesG. The folder containing our local <a.en repository is usually under our user folder and is called .m4 (for e,ample under &ni, system is /home/>my&ser ame?/.m4/repository)(
llustration *% 'etting /*DAHP" variable @nce we ha.e created the classpath .ariable we can go to F=ile/-mport.../I,isting ProHect into 2or#spaceG* select the directory of the proHect and press F=inishG(
llustration 1% mporting existing pro#ect into Hclipse without coping the original pro#ect into workspace 4see the note below5
4%A
@nce the proHect has been imported into Iclipse* we are free to use our fa.ourite plug-ins to run it or debug it (li#e for e,ample run-Hetty-run( http(//code.google.com/p/run-Hetty-run/). >ote Please note the option F!opy proHects into wor#spaceG in the pre.ious illustration. -f we select it* the original proHect generated with <a.en wonBt be affected by the changes made inside Iclipse because we will wor# on a copy of it under the current wor#space. >ote -f we modify the pom.,ml file (for e,ample adding further dependencies) we must regenerate proHectBs artifacts and refresh the proHect (=5 #ey) to reflect changes into Iclipse.
4%6
4%7
"o far we ha.e introduced only modules /ryo Seriali<er and 3ava## In-ect* but 2ic#et"tuff comes with many other modules that can be used in our applications A8. "ome of them come in handy to impro.e the user e,perience of our pages with comple, components or integrating some popular web ser.ices (li#e 0oogle <aps6%) and Ka.a"cript libraries (li#e Tiny<!I61). This appendi, pro.ides a Euic# o.er.iew of what 2ic#et"tuff offers to enhance the usability and the .isually-appealing of our pages. >ote I.ery 2ic#et"tuff module can be downloaded as KA+ archi.e at http(//m.nrepository.com. This site pro.ides also the L<: fragment needed to include it as a dependency into our pom.,ml file64.
69 The full list of the available modules can be found at https: github.com wicketstuff core wiki 70 http: maps.google.com 71 http: www.tinymce.com 72 n addition to /aven also vy6 Grape6 Gradle 6Buildr and 'BT are supported
4%8
To add more functionalities we must use class /in,MC'Settings to register additional Tiny<!I plugins and to customiCe the toolbars buttons. The following code is an e,cerpt from e,ample page F"llFeat"red/in,MC'Page(
Tin";C()ettings settings 8 neC Tin";C()ettings7Tin";C()ettings.Theme.ad4anced:1 //... // %irst toolbar //... settings.add7.utton.neCdocument, Tin";C()ettings.Toolbar.%irst, Tin";C()ettings.#osition.be%ore:1 settings.add7.utton.separator, Tin";C()ettings.Toolbar.%irst, Tin";C()ettings.#osition.be%ore:1 settings.add7.utton.%ontselect, Tin";C()ettings.Toolbar.%irst, Tin";C()ettings.#osition.a%ter:1 //... // other settings settings.setToolbar5lign7Tin";C()ettings.5lign.le%t:1 settings.setToolbar/ocation7Tin";C()ettings./ocation.top:1 settings.set)tatusbar/ocation7Tin";C()ettings./ocation.bottom:1 settings.set*esiPing7true:1 //... Text5rea text5rea 8 neC Text5rea79ta9, neC ;odel7T(]T::1 text5rea.add7neC Tin";ce.eha4ior7settings::1
=or more configuration e,amples see pages inside pac#age wicket.contrib.e(amples.tin,mce in the e,ample proHect of the module.
73 http: maps.google.com
41%
<ar#up code(
... <bod"> <di4 Cicket:id89map9>;ap</di4> </bod"> ...
Ka.a code(
public class )imple#age extends Wicket(xample#age public )imple#age7: -;ap map 8 neC -;ap79map9:1 map.set)treetOieCControl(nabled7%alse:1 map.set)caleControl(nabled7true:1 map.set)crollWheel`oom(nabled7true:1 map.setCenter7neC -/at/ng7T=.<[Z<S, @R.==BT[R::1 add7map:1 & &
The component defines a number of setters to customiCe its beha.ior and appearance. <ore info can be found on wi#i page https(//github.com/wic#etstuff/core/wi#i/0map$.
Ka.a code(
$Chart+ata data 8 neC 5bstractChart+ata7: public doubleHJHJ get+ata7: return neC doubleHJHJ & &1 Chart#ro4ider pro4ider 8 neC Chart#ro4ider7neC +imension7=TA, @AA:, ChartT"pe.#$(MR+, data:1 pro4ider.set#ie/abels7neC )tringHJ 9Dello9, 9World9 &:1 add7neC Chart79helloWorld9, pro4ider::1 R<, == & &1
Displayed chart(
74 https: developers.google.com chart
411
As we can see in the snippet abo.e* component Chart must be used with <img> tag while the input data returned by 2Chart:ata must be a two-dimensional array of double .alues.
The following snippet illustrate how to use :ata*rid https(//github.com/wic#etstuff/core/wi#i/-n<ethod0rid( <ar#up code(
...
and
is
ta#en
from
wi#i
page
414
Ka.a code(
%inal /ist<#erson> person/ist 8 //load a list o% #ersons %inal /ist+ata#ro4ider list+ata#ro4ider 8 neC /ist+ata#ro4ider7person/ist:1 //de%ine gridIs columns /ist<$-ridColumn> cols 8 7/ist: 5rra"s.as/ist7 neC #ropert"Column7neC ;odel793irst Name9:, 9%irstName9:, neC #ropert"Column7neC ;odel79/ast Name9:, 9lastName9::1 +ata-rid grid 8 neC +e%ault+ata-rid79grid9, neC +ata#ro4ider5dapter7list+ata#ro4ider:, cols:1 add7grid:1
-n the code abo.e we ha.e used con.enience class :e0a"lt:ata*rid that is a subclass of :ata*rid and it already comes with a na.igation toolbar. The e,ample pages are under pac#age com.inmethod.grid.e(amples.pages in the e,ample proHect which is hosted at http(//www.wic#et-library.com/inmethod-grid/data-grid/simple.
41$
413
7lphabetical ndex
2lpha9etical Index
A
AbstractAHa,TimerBeha.ior........................................1A6 Abstract!hec#Bo,<odel............................................1A3 Abstract+esource.......................................................1$% Abstract+esource ......................................................1$3 Abstract+oleAuthoriCation"trategy2ic#et..................175 AbstractTree...............................................................1A% Acceptance test..........................................................184 Access-denied page...................................................176 AKAL...........................................................................155 AKAL beha.iors..........................................................1A5 AKAL call listener........................................................1A8 global listeners......................................................16$ AHa,Button..................................................................15A AHa,!hec#Bo,............................................................156 AHa,Iditable!hoice:abel............................................156 AHa,Iditable:abel.......................................................156 AHa,Iditable<ulti:ine:abel........................................156 AHa,I.entBeha.ior.....................................................1AA AHa,=allbac#Button.....................................................156 AHa,=allbac#:in#.........................................................156 AHa,=orm!omponent&pdatingBeha.ior.....................1A6 AHa,=orm"ubmitBeha.ior...........................................1A6 AHa,:in#..............................................................155* 15A AHa,+eEuestAttributes................................................1A7 AHa,+eEuestTarget.............................................155* 185 AHa,"ubmit:in#...........................................................15A Annotations+oleAuthoriCation"trategy.......................176 Application............................................................3A* 137 Application"ettings.....................................................154 Attribute wic#et(id.........................................................11 Attribute<odifier...........................................................4A Authenticated2ebApplication.....................168* 17%* 177 Authenticated2eb"ession.........................................168 Authentication.............................................................168 authentication e,ample.........................................17% on!onfigure............................................................$A Auto!ompleteTe,t=ield..............................................157 Autolin# ............................................................................ boo#mar#able pages...............................................5A pac#age resources................................................1$4 !allbac# &+:s............................................................13A !aptcha-mage+esource.............................................1$% !hec#Bo,...........................................................1%4* 1A4 !hec#Bo,<ultiple!hoice............................................1%3 !hec#bo,<ultiple!hoice"elector...............................1%5 !hec#Bo,"elector......................................................1%5 !hec#ed=older...........................................................1A4 !hoice+enderer.....................................................65* 66 !lassPath+esource=inder..........................................1$6 !omponent...............................................6* A5* 135* 137 and <odel...............................................................A5 !omponent ot=oundI,ception...................................14 !ompoundAuthoriCation"trategy................................173 !ompoundProperty<odel.......................................A7* 6$ continueTo@riginalDestination...................................17% !on.erter:ocator..........................................................8% !rypto<apper...............................................................A$
D
Data grid.....................................................................414 DataDiew....................................................................11$ populate-tem.........................................................11$ DateTe,t=ield.............................................................131 Default-tem+euse"trategy.........................................11$ Default<apper!onte,t..................................................A4 Default<utableTree ode...........................................1A1 Deprecated tree components.....................................1A1 DropDown!hoice.........................................................63
I
I.ent-based component communication...................137 I,ternal:in#..................................................................57
=
=ile&pload=ield.............................................................8A =lash messages...........................................................76 =older.........................................................................1A4 =orm.............................................................................6% feedbac# messages................................................74 multipart content.....................................................8A processing...............................................................74 .alidation.................................................................74 =orm!omponent...........................................................6% =orm!omponentPanel.................................................87 =ormTester.................................................................186 select.....................................................................187 select<ultiple........................................................187 set=ile...................................................................187 setDalue................................................................187 submit...................................................................186 submit:in#.............................................................187
B
Beha.ior.................................................................4A* $7 Beha.iors....................................................................135 Boo#mar#able pages....................................................53 Boo#mar#ablePage:in#................................................5A Border...........................................................................$4 Broadcast...................................................................137 Built-in .alidators..........................................................73 Bundles loo#up algorithm...........................................144 order of tra.ersed bundles....................................145 Button...........................................................................8$
!
Wicket free user guide
415
7lphabetical ndex
0
0enerating 9T<: mar#up from code.........................154 getDefault<odel@bHect.................................................64 get<ar#up-d..................................................................46 getPage........................................................................$A getParent......................................................................$A 0oogle !hart..............................................................411 0oogle <aps..............................................................41% 0uice..........................................................................165 0uiceApplication.........................................................167 02T...............................................................................5
9
9eader contribution....................................................1$$ 9eader-tem.................................................................1$$ 9ibernate........................................................................1 9TTP"........................................................................178 9ttp"ession..................................................................5% 9ttps<apper...............................................................178
-AHa,!all:istener.........................................................1A7 -AHa,-ndicatorAware....................................................1A7 -Application"ettings......................................................33 -AuthoriCation"trategy................................................17$ -Auto!omplete+enderer.............................................157 -!haining<odel.............................................................65 -!hoice+ender..............................................................65 -!omponentAware9eader!ontributor........................161 -!omponent-nherited<odel..........................................A8 -!on.erter.....................................................................78 -!on.erter:ocator.........................................................78 -DataPro.ider..............................................................11$ -Detachable..................................................................67 -I.ent.........................................................................138 -I.ent"in#..................................................................137 -I.ent"ource..............................................................137 -=orm"ubmitter.......................................................6%* 84 -=orm"ubmitting!omponent.........................................84 -9eader!ontributor.............................................1$$* 135 -9eader+esponse.......................................................1$$ --nitialiCer....................................................................15% --tem+euse"trategy....................................................11$ -Ka.a"cript:ibrary"ettings..........................................13$ -mage..........................................................................1$1 -<apper!onte,t............................................................A1 -<ar#up+esource"treamPro.ider..............................154 -<odel...........................................................................A5 -nde,ed parameters......................................................55 -ndicatingAHa,Button...................................................1A7 -ndicatingAHa,=allbac#:in#.........................................1A7 -ndicatingAHa,:in#.......................................................1A7 -nitialiCer.....................................................................15% -nternationaliCation and <odels..................................14A in.alidate......................................................................54 in.alidate ow...............................................................54 -Pac#age+esource0uard...........................................18% -Pageable...................................................................11$ -+eEuest!ycle:istener.................................................37 -+eEuest!yclePro.ider.................................................3A -+eEuest9andler...........................................................36
Wicket free user guide
handler resol.ing algorithm.....................................36 -+eEuest:istener........................................................13A -+eEuest<apper...........................................................36 -+esource...................................................................1$% Attributes...............................................................1$% -+esource=inder.........................................................1$6 -+esource"ettings......................................145* 1$6* 1$7 get+esource=inders..............................................1$7 get"tring+esource:oaders...................................145 -+esource"tream:ocator............................................1$6 -+ole!hec#ing"trategy...............................................176 -"ecurity"ettings.........................................................177 isInabled......................................................................4A -"erialiCer......................................................................34 -"ession:istener...........................................................38 isPage"tateless............................................................3% isTemporary..................................................................51 -"tring+esource:oader...............................................145 isDisible........................................................................4A -tem.............................................................................114 -TreePro.ider..............................................................1A1 -&nauthoriCed!omponent-nstantiation:istener..........177 -Dalidatable...................................................................76 -DalidationIrror.............................................................76 -Dalidator......................................................................74
K
Ka.aBeans....................................................................A6 Ka.aII -nHect..............................................................4%8 Ka.aII -nHect .............................................................165 Ka.aII!omponent-nHector.........................................165 Ka.a"erialiCer...............................................................34 KBoss "eam...............................................................165 K<L............................................................................15% &sing K<L to control 2ic#et apps........................15% KQuery integration......................................................13% KQueryDate=ield.........................................................131 KQuery&- integration...................................................13% K"=.................................................................................5 KTree..........................................................................1A1 K&nit.......................................................................5* 184
M
Mryo proHect..................................................................34 Mryo serialiCer.......................................................34* 4%8
:
:abel...................................................................7* 11* A5 :in#.........................................................................14* 53 on!lic#....................................................................14 :istDataPro.ider.........................................................11$ :ist-tem.......................................................................111 :istDiew......................................................................111 populate-tem.........................................................111 set+euse-tems......................................................114 :oadableDetachable<odel...........................................67 :ocale.........................................................................116 :ocaliCation in 2ic#et.................................................116
41A
7lphabetical ndex
<
<ar#up file....................................................................11 <ar#up!ontainer....................................................11* 17 <a.en.............................................................7* 184* 4%$ importing proHects into Iclipse..............................4%A importing proHects into -DIA.................................4%5 importing proHects into etBeans..........................4%5 <etadata.......................................................................54 <etaDataMey................................................................54 <etaData+oleAuthoriCation"trategy..........................175 <oc#9ttp"er.let+esponse.........................................183 <odal2indow.............................................................157 Page!reator..........................................................158 2indow!losed!allbac#........................................1A% <odel............................................................................A5 model chaining........................................................65 model detaching......................................................67 model inheritance....................................................A7 using more than one model....................................7% <odel (class)................................................................AA <ounted<apper...........................................................A% mountPage...................................................................A% <ulti=ile&pload=ield.....................................................86 <D! pattern...................................................................3
Q
Query string parameters...............................................53
+
+edirecting user to an intermediate page...................174 +efreshingDiew..........................................................114 populate-tem.........................................................114 +epeaters...................................................................11% +epeatingDiew...........................................................11% +eEuest........................................................................3A +eEuest!ycle.......................................................3A* 137 hoo# methods.........................................................37 listeners...................................................................37 reEuest processing..................................................36 using a custom reEuest cycle..................................3A +eEuest:istener-nterface...........................................13A +eEuire9ttps...............................................................178 +esource management.................................................... custom resources..................................................1$3 mounting resources..............................................1$5 pac#age resources................................................1$% resource dependencies.........................................1$3 resource references..............................................1$% static .s dynamic resources..................................1$% +esourceBundle.........................................................116 +esource+eference....................................................1$% +esource+esponse....................................................1$5 2rite!allbac#........................................................1$5 +esource"tream:ocator.............................................1$6 +esponse.....................................................................3A +estart+esponseAt-nterceptPageI,ception..............174 restart+esponseAt"ign-nPage...................................17% +euse-f<odelsIEual"trategy.....................................11$ +oleAuthoriCation"trategy..........................................178 +oles..........................................................................173
@
onBefore+ender...........................................................$A on-nitialiCe.....................................................................$A on<odel!hanged.........................................................A5 on"ubmit......................................................................6% @penIKB....................................................................165
P
Pac#age<apper...........................................................A1 Pac#age+esource+eference......................................1$1 Page mounting................................................................. optional placeholders..............................................A1 placeholders............................................................A% Page serialiCation...................................................3%* 34 Page .ersioning............................................................3% disabling .ersioning................................................34 PageParameters.....................................................33* 53 Page+eference.............................................................34 Paging a.igator.........................................................113 Palette........................................................................1%6 Panel............................................................................17 PasswordTe,t=ield.......................................................64 Path............................................................................1$6 PatternDate!on.ert....................................................13$ P@K@............................................................................A6 Property<odel..............................................................A7 Property+esol.er..........................................................A7
"
"ecurePac#age+esource0uard.................................18% "erialiCable...................................................................AA "ession.................................................................38* 137 accessing to http session obHect.............................5% discarding session data..........................................54 session listener.......................................................38 setDefault<odel@bHect.................................................64 setInabled....................................................................4A set<ar#up-d..................................................................46 set@utput<ar#up-d.......................................................46 set@utput<ar#upPlaceholderTag...............................1A5 set+enderBody@nly.....................................................$% set+esponsePage..................................................1$* 37 setDersioned.................................................................34 setDisible................................................................44* 4A "hared+esource+eference........................................1$A "implePageAuthoriCation"trategy..............................173 "ortableDataPro.ider.................................................11$ "pring.............................................................1* 165* 16A "pring!omponent-nHector...........................................16A "tateful pages...............................................................3% "tateless pages................................................3%* 33* 55 "tateless:in#................................................................58 "truts..............................................................................1
416
7lphabetical ndex
T
TagTester...................................................................188 Test Dri.en De.elopment...........................................184 Testing with 2ic#et........................................................... setting form component input................................187 testing AKAL beha.iors.........................................18A testing AKAL components.....................................185 testing AKAL e.ents..............................................185 testing component status......................................183 testing components in isolation.............................183 testing feedbac# messages..................................187 testing lin#s ..........................................................18$ testing models.......................................................188 testing web response............................................183 testing 2ic#et forms..............................................186 Te,t=ield.......................................................................64 Tiny<!I.....................................................................4%8 Tree repeaters............................................................1A% Tree<odelPro.ider.....................................................1A1
&
&nit test......................................................................184 &pdating hidden components .ia AKAL.....................1A5 url=or and map&rl=or ..................................................37 &rlPathPagParametersIncoder...................................A4
D
Daadin............................................................................5
2
2ebApplication and testing.............................................. 2ebApplication.....................................................18$ 2ebApplicationPath...........................................1$6* 1$7 2eb<ar#up!ontainer.............................................46* 47 2ebPage..................................................................6* 11
2eb"ession.................................................................38 2ic#et forms.................................................................6% 2ic#et :in#s.................................................................14 2ic#et modules..............................................................6 2ic#et plugins............................................................4%6 for Iclipse.............................................................4%6 for -DIA................................................................4%6 for etBeans.........................................................4%6 Qwic#ie.................................................................4%6 2ic#et=orge..........................................................4%6 2ic#et tags....................................................................... >wic#et(body/?........................................................$4 >wic#et(border?......................................................$4 >wic#et(child?.........................................................4$ >wic#et(enclosure?.................................................$1 >wic#et(e,tend?......................................................4$ >wic#et(fragment?...................................................47 >wic#et(head?.........................................................48 >wic#et(id?................................................................A >wic#et(lin#?...................................................5A* 1$4 >wic#et(message?................................................141 >wic#et(panel?........................................................18 >wic#et(remo.e?.....................................................$% 2ic#et+untimeI,ception..............................................14 2ic#et"er.let................................................................11 2ic#et"tuff...................................................34* 165* 4%8 2ic#etTester...............................................................184 assert!omponent@nAHa,+esponse......................185 assertInabled.......................................................183 assertIrror<essages............................................187 assert=eedbac#....................................................187 assert-nfo<essages..............................................187 assert:abel...........................................................18$ assert<odelDalue.................................................188 assert+enderedPage............................................18$ assert+eEuired......................................................183 assertDisible..........................................................183 clic#:in#................................................................18$ e,ecuteAHa,I.ent.................................................18A e,ecute&rl.............................................................185 get:ast+enderedPage..........................................18$ get:ast+esponse..................................................183 is!omponent@nAHa,+esponse.............................185 new=ormTester.....................................................186 start!omponent....................................................183 start!omponent-nPage.........................................183 startPage...............................................................18$ start+esource........................................................185
417