Added: wicket/common/site/trunk/_site/guide/chapter9.html URL: http://svn.apache.org/viewvc/wicket/common/site/trunk/_site/guide/chapter9.html?rev=1527613&view=auto ============================================================================== --- wicket/common/site/trunk/_site/guide/chapter9.html (added) +++ wicket/common/site/trunk/_site/guide/chapter9.html Mon Sep 30 15:41:38 2013 @@ -0,0 +1,374 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> +<head> + <title>9 Wicket Links and URL generation 1.0.0.BUILD-SNAPSHOT</title> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + <link rel="stylesheet" href="../css/main.css" type="text/css" media="screen, print" title="Style" charset="utf-8"/> + <link rel="stylesheet" href="../css/pdf.css" type="text/css" media="print" title="PDF" charset="utf-8"/> + <script type="text/javascript"> +function addJsClass() { + var classes = document.body.className.split(" "); + classes.push("js"); + document.body.className = classes.join(" "); +} + </script> +</head> + +<body class="body" onload="addJsClass();"> +<div id="navigation"> + <ul> + <li> + <div id="nav-summary" onmouseover="toggleNavSummary(false)" onmouseout="toggleNavSummary(true)"> + <a href="../guide/index.html" class="button">Table of contents</a> + + <div id="nav-summary-childs" style="display:none;"> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter1.html"><strong>1</strong><span>Introduction</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter2.html"><strong>2</strong><span>Why should I learn Wicket?</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter3.html"><strong>3</strong><span>Wicket says “Hello world!”</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter4.html"><strong>4</strong><span>Wicket as page layout manager</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter5.html"><strong>5</strong><span>Keeping control over HTML</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter6.html"><strong>6</strong><span>Components lifecycle</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter7.html"><strong>7</strong><span>Page versioning and caching</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter8.html"><strong>8</strong><span>Under the hood of the request processing</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter9.html"><strong>9</strong><span>Wicket Links and URL generation</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter10.html"><strong>10</strong><span>Wicket models and forms</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter11.html"><strong>11</strong><span>Wicket forms in detail</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter12.html"><strong>12</strong><span>Displaying multiple items with repeaters</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter13.html"><strong>13</strong><span>Internationalization with Wicket</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter14.html"><strong>14</strong><span>Resource management with Wicket</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter15.html"><strong>15</strong><span>An example of integration with JavaScript</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter16.html"><strong>16</strong><span>Wicket advanced topics</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter17.html"><strong>17</strong><span>Working with AJAX</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter18.html"><strong>18</strong><span>Integration with enterprise containers</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter19.html"><strong>19</strong><span>Security with Wicket</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter20.html"><strong>20</strong><span>Test Driven Development with Wicket</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter21.html"><strong>21</strong><span>Test Driven Development with Wicket and Spring</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter22.html"><strong>22</strong><span>Wicket Best Practices</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter23.html"><strong>23</strong><span>Working with Maven (Appendix)</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter24.html"><strong>24</strong><span>Project WicketStuff (Appendix)</span></a> + </div> + + <div class="toc-item" style="margin-left:0"><a href="../guide/chapter25.html"><strong>25</strong><span>Lost In Redirection With Apache Wicket (Appendix)</span></a> + </div> + + </div> + </div> + </li> + <li class="separator selected"> + <a id="ref-button" onclick="localToggle(); return false;" href="#">Quick Reference</a> + </li> + </ul> +</div> +<div id="header"> + <div class="images clearfix"> + + <span id="logo"><a href="/" target="_blank"><img height="80px" src="http://comsysto.github.io/wicket-userguide/img/apache-wicket.png"/></a></span> + + + <span id="sponsor"><a href="http://www.comsysto.com/" target="_blank"><img height="60px" src="http://comsysto.github.io/wicket-userguide/img/comsysto-logo.png"/></a></span> + + </div> + <p>Free Online Guide for Apache Wicket framework</p> +</div> + + +<table id="colset" border="0" cellpadding="0" cellspacing="0"> + <tr> + <td id="col1"> + <div id="main" class="corner-all"> + + + <div class="toc-item prev-left"><a href="../guide/chapter8.html"><< <strong>8</strong><span>Under the hood of the request processing</span></a></div> + + + <span id='toggle-col1' class="toggle">(<a href="#" onclick="localToggle(); return false;">Quick Reference</a>)</span> + + + <div class="toc-item next-right"><a href="../guide/chapter10.html"><strong>10</strong><span>Wicket models and forms</span> >></a></div> + + + + <div class="project"> + <h1>9 Wicket Links and URL generation - Reference Documentation</h1> + + <p><strong>Authors:</strong> Andrea Del Bene, Carsten Hufe, Christian Kroemer, Daniel Bartl</p> + + <p><strong>Version:</strong> 1.0.0.BUILD-SNAPSHOT</p> + + + </div> + + + <div id="table-of-content"> + <h2>Table of Contents</h2> + + <div class="toc-item" style="margin-left:0px"><a href="#chapter9_1"><strong>9.1</strong><span>PageParameters</span></a> + </div> + + <div class="toc-item" style="margin-left:0px"><a href="#chapter9_2"><strong>9.2</strong><span>Bookmarkable links</span></a> + </div> + + <div class="toc-item" style="margin-left:0px"><a href="#chapter9_3"><strong>9.3</strong><span>Automatically creating bookmarkable links with tag <wicket:link></span></a> + </div> + + <div class="toc-item" style="margin-left:0px"><a href="#chapter9_4"><strong>9.4</strong><span>External links</span></a> + </div> + + <div class="toc-item" style="margin-left:0px"><a href="#chapter9_5"><strong>9.5</strong><span>Stateless links</span></a> + </div> + + <div class="toc-item" style="margin-left:0px"><a href="#chapter9_6"><strong>9.6</strong><span>Generating structured and clear URLs</span></a> + </div> + + <div class="toc-item" style="margin-left:0px"><a href="#chapter9_7"><strong>9.7</strong><span>Summary</span></a> + </div> + + </div> + + + + +<h1 id="chapter9">9 Wicket Links and URL generation</h1> +Up to now we used component Link to move from a page to another and we have seen that it is quiet similar to a âclickâ event handler (see paragraph 2.4).<p class="paragraph"/>However this component alone is not enough to build all possible kinds of links we may need in our pages. Therefore, Wicket offers other link components suited for those tasks which can not be accomplished with a basic Link.<p class="paragraph"/>Besides learning new link components, in this chapter we will also see how to customize the page URL generated by Wicket using the encoding facility provided by the framework and the page parameters that can be passed to a target page. + + + +<h2 id="chapter9_1">9.1 PageParameters</h2> +<p class="paragraph"/>A common practice in web development is to pass data to a page using query string parameters (like ?paramName1=paramValu1&paramName2=paramValue2...). Wicket offers a more flexible and object oriented way to do this with models (we will see them in the next chapter). However, even if we are using Wicket, we still need to use query string parameters to exchange data with other Internet-based services. Consider for example a classic confirmation page which is linked inside an email to let users confirm important actions like password changing or the subscription to a mailing list. This kind of page usually expects to receive a query string parameter containing the id of the action to confirm.<p class="paragraph"/>Query string parameters can also be referred to as named parameters. In Wicket they are handled with class org.apache.wicket.request.mapper.parameter.PageParameters. Since named parameters are basically name-value pairs, PageParameters works in much t he same way as Java Map providing two methods to create/modify a parameter (add(String name, Object value) and set(String name, Object value)), one method to remove an existing parameter (remove(String name)) and one to retrieve the value of a given parameter (get(String name)) . Here is a snippet to illustrate the usage of PageParameters:<p class="paragraph"/><div class="code"><pre>PageParameters pageParameters = <span class="java-keyword">new</span> PageParameters(); +//add a couple of parameters +pageParameters.add(<span class="java-quote">"name"</span>, <span class="java-quote">"John"</span>); +pageParameters.add(<span class="java-quote">"age"</span>, 28); +//retrieve the value of 'age' parameter +pageParameters.get(<span class="java-quote">"age"</span>);</pre></div><p class="paragraph"/>Now that we have seen how to work with page parameters, let's see how to use them with our pages.<p class="paragraph"/><h3>PageParameters and bookmarkable pages</h3><p class="paragraph"/>Base class Page comes with a constructor which takes as input a PageParameters instance. If we use this superclass constructor in our page, PageParameters will be used to build the page URL and it can be retrieved at a later time with the Page's getPageParameters() method.<p class="paragraph"/>In the following example taken from the PageParametersExample project we have a home page with a link to a second page that uses a version of setResponsePage method that takes as input also a PageParameters to build the target page (named PageWithParameters). The code for the link and for the target page is the following:<p class="paragraph"/>Link code:<p class="paragraph"/><div class="code"><pre>add(<span class="ja va-keyword">new</span> Link(<span class="java-quote">"pageWithIndexParam"</span>) {<p class="paragraph"/> @Override + <span class="java-keyword">public</span> void onClick() {<p class="paragraph"/> PageParameters pageParameters = <span class="java-keyword">new</span> PageParameters(); + pageParameters.add(<span class="java-quote">"foo"</span>, <span class="java-quote">"foo"</span>); + pageParameters.add(<span class="java-quote">"bar"</span>, <span class="java-quote">"bar"</span>);<p class="paragraph"/> setResponsePage(PageWithParameters.class, pageParameters); + }<p class="paragraph"/>});</pre></div><p class="paragraph"/>Target page code:<p class="paragraph"/><div class="code"><pre><span class="java-keyword">public</span> class PageWithParameters <span class="java-keyword">extends</span> WebPage { + //Override superclass constructor + <span class="java-keyword">public</span> PageWithParameters(PageParameters parameters) { + <span class="java-keyword">super</span>(parameters); + } + }</pre></div><p class="paragraph"/>The code is quite straightforward and itâs more interesting to look at the URL generated for the target page:<p class="paragraph"/><div class="code"><pre><app root>/PageParametersExample/wicket/bookmarkable/org.wicketTutorial.PageWithParameters?foo=foo&bar=bar</pre></div><p class="paragraph"/>At first glance the URL above could seem a little weird, except for the last part which contains the two named parameters used to build the target page.<p class="paragraph"/>The reason for this âstrangeâ URL is that, as we explained in paragraph 6.2.5, when a page is instantiated using a constructor with no argument or using a constructor that accepts only a PageParameters, Wicket will try to generate a static URL for it, with no session-relative informations. This kind of URL is called bookmarkable because it can be saved by the users as a bookmark and accessed at a later time.<p class="paragraph"/>A bookmarkable URL is compos ed by a fixed prefix (which by default is bookmarkable) and the qualified name of the page class (org.wicketTutorial.PageWithParameters in our example). Segment wicket is another fixed prefix added by default during URL generation. In paragraph 8.6.4 we will see how to customize fixed prefixes with a custom implementation of IMapperContext interface.<p class="paragraph"/><h3>Indexed parameters</h3><p class="paragraph"/>Besides named parameters, Wicket also supports indexed parameters. These kinds of parameters are rendered as URL segments placed before named parameters. Let's consider for example the following URL:<p class="paragraph"/><div class="code"><pre><application path>/foo/bar?1&baz=baz</pre></div><p class="paragraph"/>The URL above contains two indexed parameters (foo and bar) and a query string consisting of the page id and a named parameter (baz). Just like named parameters also indexed parameters are handled by the PageParameters class. The methods provided b y PageParameters for indexed parameters are set(int index, Object object) (to add/modify a parameter), remove(int index)(to remove a parameter) and get(int index) (to read a parameter).<p class="paragraph"/>As their name suggests, indexed parameters are identified by a numeric index and they are rendered following the order in which they have been added to the PageParameters. The following is an example of indexed parameters:<p class="paragraph"/><div class="code"><pre>PageParameters pageParameters = <span class="java-keyword">new</span> PageParameters(); +//add a couple of parameters +pageParameters.set(0, <span class="java-quote">"foo"</span>); +pageParameters.set(1, <span class="java-quote">"bar"</span>); +//retrieve the value of the second parameter (<span class="java-quote">"bar"</span>) +pageParameters.get(1);</pre></div><p class="paragraph"/>Project PageParametersExample comes also with a link to a page with both indexed parameters and a named parameter:<p class="paragraph"/><div class="code"><pre>add(<span class="java-keyword">new</span> Link(<span class="java-quote">"pageWithNamedIndexParam"</span>) {<p class="paragraph"/> @Override + <span class="java-keyword">public</span> void onClick() {<p class="paragraph"/> PageParameters pageParameters = <span class="java-keyword">new</span> PageParameters(); + pageParameters.set(0, <span class="java-quote">"foo"</span>); + pageParameters.set(1, <span class="java-quote">"bar"</span>); + pageParameters.add(<span class="java-quote">"baz"</span>, <span class="java-quote">"baz"</span>);<p class="paragraph"/> setResponsePage(PageWithParameters.class, pageParameters); + }<p class="paragraph"/>});</pre></div><p class="paragraph"/>The URL generated for the linked page (PageWithParameters) is the one seen at the beginning of the paragraph. + + +<h2 id="chapter9_2">9.2 Bookmarkable links</h2> +<p class="paragraph"/>A link to a bookmarkable page can be built with the link component org.apache.wicket.markup.html.link.BookmarkablePageLink:<p class="paragraph"/><div class="code"><pre>BookmarkablePageLink bpl=<span class="java-keyword">new</span> BookmarkablePageLink(PageWithParameters.class, pageParameters);</pre></div><p class="paragraph"/>The specific purpose of this component is to provide an anchor to a bookmarkable page, hence we don't have to implement any abstract method like we do with Link component. + + +<h2 id="chapter9_3">9.3 Automatically creating bookmarkable links with tag <wicket:link></h2> +<p class="paragraph"/>Bookmarkable pages can be linked directly inside markup files without writing any Java code. Using <wicket:link> tag we ask Wicket to automatically add bookmarkable links for the anchors wrapped inside it. Here is an example of usage of <wicket:link> tag taken from the home page of the project BookmarkablePageAutoLink:<p class="paragraph"/><div class="code"><pre><!DOCTYPE html> +<html xmlns:wicket=<span class="java-quote">"http://wicket.apache.org"</span>> + <head> + <meta charset=<span class="java-quote">"utf-8"</span> /> + <title>Apache Wicket Quickstart</title> + </head> + <body> + <div id=<span class="java-quote">"bd"</span>> + <wicket:link> + <a href=<span class="java-quote">"HomePage.html"</span>>HomePage</a><br/> + <a href=<span class="java-quote">"anotherPackage/SubPackagePage.html"</span>>SubPackagePage</a> + </wicket:link> + </div> + </body> +</html></pre></div><p class="paragraph"/>The key part of the markup above is the href attribute which must contain the package-relative path to a page. The home page is inside package org.wicketTutorial which in turns contains the sub package anotherPackage. This package hierarchy is reflected by the href attributes: in the first anchor we have a link to the home page itself while the second anchor points to page SubPackagePage which is placed into sub package anotherPackage. Absolute paths are supported as well and we can use them if we want to specify the full package of a given page. For example the link to SubPackagePage could have been written in the following (more verbose) way:<p class="paragraph"/><div class="code"><pre><a href=<span class="java-quote">"/org/wicketTutorial/anotherPackage/SubPackagePage.html"</span>>SubPackagePage</a></pre></div><p class="paragraph"/>If we take a look also at the markup of SubPackagePage we can see that it contains a link to the home page which uses the parent directory selector (relative path):<p class="paragraph"/><div class="code"><pre><!DOCTYPE html> +<html xmlns:wicket=<span class="java-quote">"http://wicket.apache.org"</span>> + <head> + <meta charset=<span class="java-quote">"utf-8"</span> /> + <title>Apache Wicket Quickstart</title> + </head> + <body> + <div id=<span class="java-quote">"bd"</span>> + <wicket:link> + <a href=<span class="java-quote">"../HomePage.html"</span>>HomePage</a><br/> + <a href=<span class="java-quote">"SubPackagePage.html"</span>>SubPackagePage</a> + </wicket:link> + </div> + </body> +</html></pre></div><p class="paragraph"/>Please note that any link to the current page (aka self link) is disabled. For example in the home page the self link is rendered like this:<p class="paragraph"/><div class="code"><pre><span><em>HomePage</em></span></pre></div><p class="paragraph"/>The markup used to render disabled links can be customized using the markup settings (interface IMarkupSettings) available in the application class:<p class="paragraph"/><div class="code"><pre>@Override +<span class="java-keyword">public</span> void init() +{ + <span class="java-keyword">super</span>.init(); + //wrap disabled links with <b> tag + getMarkupSettings().setDefaultBeforeDisabledLink(<span class="java-quote">"<b>"</span>); + getMarkupSettings().setDefaultAfterDisabledLink(<span class="java-quote">"</b>"</span>); +}</pre></div><p class="paragraph"/>The purpose of <wicket:link> tag is not limited to just simplifying the usage of bookmarkable pages. As we we will see in chapter 13, this tag can also be adopted to manage web resources like pictures, CSS files, JavaScript files and so on. + + +<h2 id="chapter9_4">9.4 External links</h2> +<p class="paragraph"/>Since Wicket uses plain HTML markup files as templates, we can place an anchor to an external page directly inside the markup file. When we need to dynamically generate external anchors, we can use link component org.apache.wicket.markup.html.link.ExternalLink. In order to build an external link we must specify the value of the href attribute using a model or a plain string. In the next snippet, given an instance of Person, we generate a Google search query for its full name:<p class="paragraph"/>Html:<p class="paragraph"/><div class="code"><pre><a wicket:id=<span class="java-quote">"externalSite"</span>>Search me on Google!</a></pre></div><p class="paragraph"/>Java code:<p class="paragraph"/><div class="code"><pre>Person person = <span class="java-keyword">new</span> Person(<span class="java-quote">"John"</span>, <span class="java-quote">"Smith"</span>); +<span class="java-object">String</span> fullName = person.getFullName(); +//Space characters must be replaced by character '+' +<span class="java-object">String</span> googleQuery = <span class="java-quote">"http://www.google.com/search?q="</span> + fullName.replace(<span class="java-quote">" "</span>, <span class="java-quote">"+"</span>); +add(<span class="java-keyword">new</span> ExternalLink(<span class="java-quote">"externalSite"</span>, googleQuery));</pre></div><p class="paragraph"/>Generated anchor:<p class="paragraph"/><div class="code"><pre><a href=<span class="java-quote">"http://www.google.com/search?q=John+Smith"</span>>Search me on Google!</a></pre></div><p class="paragraph"/>If we need to specify a dynamic value for the text inside the anchor, we can pass it as an additional constructor parameter:<p class="paragraph"/>Html:<p class="paragraph"/><div class="code"><pre><a wicket:id=<span class="java-quote">"externalSite"</span>>Label goes here...</a></pre></div><p class="paragraph"/>Java code:<p class="paragraph"/><div class="code"><pre>Person person = <span class="java-keyword">new</span> Person(<span class="java-quote">"John"</span>, <span class="java-quote">"Smith"</span>); +<span class="java-object">String</span> fullName = person.getFullName(); +<span class="java-object">String</span> googleQuery = <span class="java-quote">"http://www.google.com/search?q="</span> + fullName.replace(<span class="java-quote">" "</span>, <span class="java-quote">"+"</span>); +<span class="java-object">String</span> linkLabel = <span class="java-quote">"Search '"</span> + fullName + <span class="java-quote">"' on Google."</span>;<p class="paragraph"/>add(<span class="java-keyword">new</span> ExternalLink(<span class="java-quote">"externalSite"</span>, googleQuery, linkLabel));</pre></div><p class="paragraph"/>Generated anchor:<p class="paragraph"/><div class="code"><pre><a href=<span class="java-quote">"http://www.google.com/search?q=John+Smith"</span>>Search 'John Smith' on Google.</a></pre></div> + + +<h2 id="chapter9_5">9.5 Stateless links</h2> +<p class="paragraph"/>Component Link has a stateful nature, hence it cannot be used with stateless pages. To use links with these kinds of pages Wicket provides the convenience org.apache.wicket.markup.html. link.StatelessLink component which is basically a subtype of Link with the stateless hint set to true.<p class="paragraph"/>Please keep in mind that Wicket generates a new instance of a stateless page also to serve stateless links, so the code inside the onClick() method can not depend on instance variables. To illustrate this potential issue let's consider the following code (from the project StatelessPage) where the value of the variable index is used inside onclick():<p class="paragraph"/><div class="code"><pre><span class="java-keyword">public</span> class StatelessPage <span class="java-keyword">extends</span> WebPage { + <span class="java-keyword">private</span> <span class="java-object">int</span> index = 0;<p class="paragraph"/> <span class="java-keyword">public</span> StatelessPage(PageParameters parameters) { + <span class="java-keyword">super</span>(parameters); + }<p class="paragraph"/> @Override + <span class="java-keyword">protected</span> void onInitialize() { + <span class="java-keyword">super</span>.onInitialize(); + setStatelessHint(<span class="java-keyword">true</span>);<p class="paragraph"/> add(<span class="java-keyword">new</span> StatelessLink(<span class="java-quote">"statelessLink"</span>) {<p class="paragraph"/> @Override + <span class="java-keyword">public</span> void onClick() { + //It will always print zero + <span class="java-object">System</span>.out.println(index++); + }<p class="paragraph"/> }); + } +}</pre></div><p class="paragraph"/>The printed value will always be zero because a new instance of the page is used every time the user clicks on the statelessLink link. + + +<h2 id="chapter9_6">9.6 Generating structured and clear URLs</h2> +<p class="paragraph"/>Having structured URLs in our site is a basic requirement if we want to build an efficient SEO1 strategy, but it also contributes to improve user experience with more intuitive URLs. Wicket provides two different ways to control URL generation. The first (and simplest) is to âmountâ one or more pages to an arbitrary path, while a more powerful technique is to use custom implementations of IMapperContext and IPageParametersEncoder interfaces. In the next paragraphs we will learn both of these two techniques.<p class="paragraph"/><h3>Mounting a single page</h3><p class="paragraph"/>With Wicket we can mount a page to a given path in much the same way as we map a servlet filer to a desired path inside file web.xml (see page 9). Using mountPage(String path, Class <T> pageClass) method of the WepApplication class we tell Wicket to respond with a new instance of pageClass whenever a user navigates to the given path. In the application class of the project MountedPagesExample we mount MountedPage to the "/pageMount" path:<p class="paragraph"/><div class="code"><pre>@Override +<span class="java-keyword">public</span> void init() +{ + <span class="java-keyword">super</span>.init(); + mountPage(<span class="java-quote">"/pageMount"</span>, MountedPage.class); + //Other initialization code… +}</pre></div><p class="paragraph"/>The path provided to mountPage will be used to generate the URL for any page of the specified class:<p class="paragraph"/><div class="code"><pre>//it will <span class="java-keyword">return</span> <span class="java-quote">"/pageMount"</span> +RequestCycle.get().urlFor(MountedPage.class);</pre></div><p class="paragraph"/>Under the hood the mountPage method mounts an instance of the request mapper org.apache. wicket.request.mapper.MountedMapper configured for the given path:<p class="paragraph"/><div class="code"><pre><span class="java-keyword">public</span> <span class="java-keyword">final</span> <T <span class="java-keyword">extends</span> Page> void mountPage(<span class="java-keyword">final</span> <span class="java-object">String</span> path,<span class="java-keyword">final</span> <span class="java-object">Class</span><T> pageClass) { + mount(<span class="java-keyword">new</span> MountedMapper(path, pageClass)); +}</pre></div><p class="paragraph"/>Request mappers and the Application's method mount have been introduced in the previous chapter (paragraph 7.3.1).<p class="paragraph"/><h3>Using parameter placeholders with mounted pages</h3><p class="paragraph"/>The path specified for mounted pages can contain dynamic segments which are populated with the values of the named parameters used to build the page. These segments are declared using special segments called parameter placeholders. Consider the path used in the following example:<p class="paragraph"/><div class="code"><pre>mountPage(<span class="java-quote">"/pageMount/${foo}/otherSegm"</span>, MountedPageWithPlaceholder.class);</pre></div><p class="paragraph"/>The path used above is composed by three segments: the first and the last are fixed while the second will be replaced by the value of the named parameter foo that must be provided when the page MountedPageWithPlaceholder is instantiated:<p class="paragraph"/>Jav a code:<p class="paragraph"/><div class="code"><pre>PageParameters pageParameters = <span class="java-keyword">new</span> PageParameters(); +pageParameters.add(<span class="java-quote">"foo"</span>, <span class="java-quote">"foo"</span>);<p class="paragraph"/>setResponsePage(MountedPageWithPlaceholder.class, pageParameters)</pre></div><p class="paragraph"/>Generated URL:<p class="paragraph"/><div class="code"><pre><Application path>/pageMount/foo/otherSegm</pre></div><p class="paragraph"/>On the contrary if we manually insert an URL like '<web app path>/pageMount/bar/otherSegm', we can read value 'bar' retrieving the named parameter foo inside our page.<p class="paragraph"/>Place holders can be declared as optional using the '#' character in place of '$':<p class="paragraph"/><div class="code"><pre>mountPage(<span class="java-quote">"/pageMount/#{foo}/otherSegm"</span>, MountedPageOptionalPlaceholder.class);</pre></div><p class="paragraph"/>If the named parameter for an optional placeholder is missing, the corresponding segment is removed from the final URL:<p class="paragraph"/> Java code:<p class="paragraph"/><div class="code"><pre>PageParameters pageParameters = <span class="java-keyword">new</span> PageParameters(); +setResponsePage(MountedPageWithPlaceholder.class, pageParameters);</pre></div><p class="paragraph"/>Generated URL:<p class="paragraph"/><div class="code"><pre><Application path>/pageMount/otherSegm</pre></div><p class="paragraph"/><h3>Mounting a package</h3><p class="paragraph"/>In addition to mounting a single page, Wicket allows to mount all of the pages inside a package to a given path. Method mountPackage(String path, Class<T> pageClass) of class WepApplication will mount every page inside pageClass's package to the specified path.<p class="paragraph"/>The resulting URL for package-mounted pages will have the following structure:<p class="paragraph"/><div class="code"><pre><Application path>/mountedPath/<PageClassName>[optional query string]</pre></div><p class="paragraph"/>For example in the MountedPagesExample project we have mounted all pages inside the subpackage org.tutorialWicket.subPackage with this line of code:<p class="para graph"/><div class="code"><pre>mountPackage(<span class="java-quote">"/mountPackage"</span>, StatefulPackageMount.class);</pre></div><p class="paragraph"/>StatefulPackageMount is one of the pages placed into the desired package and its URL will be:<p class="paragraph"/><div class="code"><pre><Application path>/mountPackage/StatefulPackageMount?1</pre></div><p class="paragraph"/>Similarly to what is done by the mountPage method, the implementation of the mountPackage method mounts an instance of org.apache.wicket.request.mapper.PackageMapper to the given path.<p class="paragraph"/><h3>Providing custom mapper context to request mappers</h3><p class="paragraph"/>Interface org.apache.wicket.request.mapper.IMapperContext is used by request mappers to create new page instances and to retrieve static URL segments used to build and parse page URLs. Here is the list of these segments: +<ul class="star"> +<li>Namespace: it's the first URL segment of non-mounted pages. By default its value is wicket.</li> +<li>Identifier for non-bookmarkable URLs: it's the segment that identifies non bookmarkable pages. By default its value is page.</li> +<li>Identifier for bookmarkable URLs: it's the segment that identifies bookmarkable pages. By default its value is bookmarkable (as we have seen before in paragraph 8.1.1).</li> +<li>Identifier for resources: it's the segment that identifies Wicket resources. Its default value is resources. The topic of resource management will be covered in chapter 13.</li> +</ul><p class="paragraph"/>IMapperContext provides a getter method for any segment listed above. By default Wicket uses class org.apache.wicket.DefaultMapperContext as mapper context.<p class="paragraph"/>Project CustomMapperContext is an example of customization of mapper context where we use index as identifier for non-bookmarkable pages and staticURL as identifier for bookmarkable pages. In this project, instead of implementing our mapper context from scratch, we used DefaultMapperContext as base class overriding just the two methods we need to achieve the desired result (getBookmarkableIdentifier() and getPageIdentifier()). The final implementation is the following:<p class="paragraph"/><div class="code"><pre><span class="java-keyword">public</span> class CustomMapperContext <span class="java-keyword">extends</span> DefaultMapperContext{<p class="paragraph"/> @Override + <span class="java-keyword">public</span> <span class="java-object">String</span> getBookmarkableIdentifier() { + <span class="java-keyword">return</span> <span class="java-quote">"staticURL"</span>; + }<p class="paragraph"/> @Override + <span class="java-keyword">public</span> <span class="java-object">String</span> getPageIdentifier() { + <span class="java-keyword">return</span> <span class="java-quote">"index"</span>; + } +}</pre></div><p class="paragraph"/>Now to use a custom mapper context in our application we must override the newMapperContext() method declared in the Application class and make it return our custom implementation of IMapperContext:<p class="paragraph"/><div class="code"><pre>@Override +<span class="java-keyword">protected</span> IMapperContext newMapperContext() { + <span class="java-keyword">return</span> <span class="java-keyword">new</span> CustomMapperContext(); +}</pre></div><p class="paragraph"/><h3>Controlling how page parameters are encoded with IPageParametersEncoder</h3><p class="paragraph"/>Some request mappers (like MountedMapper and PackageMapper) can delegate page parameters encoding/decoding to interface org.apache.wicket.request.mapper.parameter.IPage ParametersEncoder. This entity exposes two methods: encodePageParameters() and decodePageParameters(): the first one is invoked to encode page parameters into an URL while the second one extracts parameters from the URL.<p class="paragraph"/>Wicket comes with a built-in implementation of this interface which encodes named page parameters as URL segments using the following patter: /paramName1/paramValue1/paramName2/param Value2...<p class="paragraph"/>This built-in encoder is org.apache.wicket.request.mapper.parameter.UrlPathPage ParametersEncoder class. In the PageParametersEncoderExample project we have manually mounted a MountedMapper that takes as input also an UrlPathPag eParametersEncoder:<p class="paragraph"/><div class="code"><pre>@Override +<span class="java-keyword">public</span> void init() { + <span class="java-keyword">super</span>.init(); + mount(<span class="java-keyword">new</span> MountedMapper(<span class="java-quote">"/mountedPath"</span>, MountedPage.class, <span class="java-keyword">new</span> UrlPathPageParametersEncoder())); +}</pre></div><p class="paragraph"/>The home page of the project contains just a link to the MountedPage web page. The code of the link and the resulting page URL are:<p class="paragraph"/>Link code:<p class="paragraph"/><div class="code"><pre>add(<span class="java-keyword">new</span> Link(<span class="java-quote">"mountedPage"</span>) {<p class="paragraph"/> @Override + <span class="java-keyword">public</span> void onClick() {<p class="paragraph"/> PageParameters pageParameters = <span class="java-keyword">new</span> PageParameters(); + pageParameters.add(<span class="java-quote">"foo"</span>, <span class="java-quote">"foo"</span>); + pageParameters.add(<span class="java-quote">"bar"</span>, <span class="java-quote">"bar"</span>);<p class="paragraph"/> setResponsePage(MountedPage.class, pageParameters); + } +});</pre></div><p class="paragraph"/>Generated URL:<p class="paragraph"/><div class="code"><pre><Application path>/mountedPath/foo/foo/bar/bar?1</pre></div><p class="paragraph"/><h3>Encrypting page URLs</h3><p class="paragraph"/>Sometimes URLs are a doubleâedged sword for our site because they can expose too many details about the internal structure of our web application and malicious users could exploit them to perform a <a href="http://en.wikipedia.org/wiki/Cross-site_request_forgery" target="blank">cross-site request forgery</a> .<p class="paragraph"/>To avoid this kind of security threat we can use the CryptoMapper request mapper which wraps an existing mapper and encrypts the original URL producing a single encrypted segment:<p class="paragraph"/><img border="0" class="center" src="../img/url-encrypted.png"></img><p class="paragraph"/>Typically, CryptoMapper is registered into a Wicket application as the root request mapper wrapping the default one:<p class=" paragraph"/><div class="code"><pre>@Override +<span class="java-keyword">public</span> void init() { + <span class="java-keyword">super</span>.init(); + setRootRequestMapper(<span class="java-keyword">new</span> CryptoMapper(getRootRequestMapper(), <span class="java-keyword">this</span>)); + //pages and resources must be mounted after we have set CryptoMapper + mountPage(<span class="java-quote">"/foo/"</span>, HomePage.class);</pre></div><p class="paragraph"/>As pointed out in the code above, pages and resources must be mounted after having set CryptoMapper as root mapper, otherwise the mounted paths will not work. + + +<h2 id="chapter9_7">9.7 Summary</h2> +<p class="paragraph"/>Links and URLs are not trivial topics as they may seem and in Wicket they are strictly interconnected. Developers must choose the right trade-off between producing structured URLs and avoiding to make them verbose and vulnerable.<p class="paragraph"/>In this chapter we have explored the tools provided by Wicket to control how URLs are generated. We have started with static URLs for bookmarkable pages and we have seen how to pass parameters to target pages with PageParameters. In the second part of the chapter we focused on mounting pages to a specific path and on controlling how parameters are encoded by Wicket. Finally, we have also seen how to encrypt URLs to prevent security vulnerabilities. + + + <div style="clear:both;margin-top:15px;"></div> + + <div class="toc-item prev-left"><a href="../guide/chapter8.html"><< <strong>8</strong><span>Under the hood of the request processing</span></a></div> + + <div class="toc-item next-right"><a href="../guide/chapter10.html"><strong>10</strong><span>Wicket models and forms</span> >></a></div> + + <div style="clear:both"></div> + </div> + </td> + <td id="col2"> + <div class="local clearfix"> + <div class="local-title"> + <a href="../guide/index.html" target="mainFrame">Quick Reference</a> + <span class="toggle">(<a href="#" onclick="localToggle(); return false;">hide</a>)</span> + </div> + <div class="menu"> + + </div> + </div> + </td> + </tr> +</table> + +<div id="footer"> + +Copyright © 2013 â <a href="http://www.comsysto.com" target="_blank">comSysto GmbH</a> +<script> + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-43124634-1', 'comsysto.com'); + ga('send', 'pageview'); + +</script> + + +</div> + +<script type="text/javascript" src="../js/docs.js"></script> + +</body> +</html>
