Modified: websites/production/tapestry/content/page-navigation.html
==============================================================================
--- websites/production/tapestry/content/page-navigation.html (original)
+++ websites/production/tapestry/content/page-navigation.html Tue Sep 26
19:20:27 2017
@@ -27,16 +27,6 @@
</title>
<link type="text/css" rel="stylesheet" href="/resources/space.css" />
- <link href='/resources/highlighter/styles/shCoreCXF.css'
rel='stylesheet' type='text/css' />
- <link href='/resources/highlighter/styles/shThemeCXF.css' rel='stylesheet'
type='text/css' />
- <script src='/resources/highlighter/scripts/shCore.js'
type='text/javascript'></script>
- <script src='/resources/highlighter/scripts/shBrushJava.js'
type='text/javascript'></script>
- <script src='/resources/highlighter/scripts/shBrushXml.js'
type='text/javascript'></script>
- <script src='/resources/highlighter/scripts/shBrushPlain.js'
type='text/javascript'></script>
- <script>
- SyntaxHighlighter.defaults['toolbar'] = false;
- SyntaxHighlighter.all();
- </script>
<link href="/styles/style.css" rel="stylesheet" type="text/css"/>
@@ -77,109 +67,20 @@
</div>
<div id="content">
- <div id="ConfluenceContent"><div class="aui-label"
style="float:right" title="Related Articles">
-
-
-
-
-
-
-
-
-<h3>Related Articles</h3>
-
-<ul class="content-by-label"><li>
- <div>
- <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
-
- <div class="details">
- <a href="content-type-and-markup.html">Content Type
and Markup</a>
-
-
- </div>
- </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
-
- <div class="details">
- <a href="page-navigation.html">Page Navigation</a>
-
-
- </div>
- </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
-
- <div class="details">
- <a href="page-life-cycle.html">Page Life Cycle</a>
-
-
- </div>
- </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
-
- <div class="details">
- <a href="component-rendering.html">Component
Rendering</a>
-
-
- </div>
- </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
-
- <div class="details">
- <a href="component-events.html">Component Events</a>
-
-
- </div>
- </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
-
- <div class="details">
- <a href="component-events-faq.html">Component Events
FAQ</a>
-
-
- </div>
- </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
-
- <div class="details">
- <a href="request-processing.html">Request
Processing</a>
-
-
- </div>
- </li></ul>
-</div>
-
-
-<p>In essence, a Tapestry application is a number of related pages, working
together. To some degree, each page is like an application unto
itself.</p><p>Any individual request will be targeted at a single page.
Requests come in two forms: </p><ul><li><em>component event</em> requests
target a specific component on a specific page, triggering an event within that
component</li><li><em>render</em> requests target a specific page, and stream
the HTML markup for that page back to the client</li></ul><p>This dichotomy
between component event requests and render requests alleviates a number of
problems in traditional web applications related to the browser back button, or
to the user hitting the refresh button in their browser.</p><p><br
clear="none"><span style="color: rgb(83,145,38);font-size: 20.0px;line-height:
1.5;">Logical Page Name Shortening</span></p><p>In certain cases, Tapestry will
shorten the the logical name of a page. For example, the page class
org.example.pages.addr
ess.CreateAddress will be given a logical name of "address/Create" (the
redundant "Address" is removed as a suffix). However, this only affects how the
page is referenced in URLs; the template file will still be CreateAddress.tml,
whether on the classpath, or as address/CreateAddress.tml (in the web
context).</p><p><span>Tapestry actually creates multiple names for the name
page: "address/Create" and "address/CreateAddress" are both synonymous. You can
user either in Java code that refers to a page by name, or as the page
parameter of a PageLink.</span></p><h2
id="PageNavigation-ComponentEventRequests&Responses">Component Event
Requests & Responses</h2><p>Main Article: <a
href="component-events.html">Component Events</a></p><p>Component event
requests may take the form of hyperlinks (<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/EventLink.html">EventLink</a>
or <a class="external-link" href="http://tapest
ry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/ActionLink.html">ActionLink</a>)
or form submissions (<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Form.html">Form</a>).</p><p>The
value returned from an <a href="component-events.html">event handler
method</a> controls the response sent to the client web browser.</p><p>The URL
for a component event request identifies the name of the page, the nested id of
the component, and the name of the event to trigger on the component (specified
by the "event" parameter of EventLink, or "action" for an ActionLink). Further,
a component event request may contain additional context information, which
will be provided to the event handler method.</p><p>These URLs expose a bit of
the internal structure of the application. Over time, as an application grows
and is maintained, the ids of components may change. This means that component
event request URLs should
not be bookmarked. Fortunately, users will rarely have the chance to do so
(see below).</p><h3 id="PageNavigation-1.Nullresponse">1. Null
response</h3><p>If the event handler method returns no value, or returns null,
then the current page (the page containing the component) will render the
response.</p><p>A page render URL for the current page is created and sent to
the client as a client side redirect. The client browser will automatically
submit a new request to generate the page.</p><p>The user will see the newly
generated content in their browser. In addition, the URL in the browser's
address bar will be a render request URL. Render request URLs are shorter and
contain less application structure (for instance, they don't include component
ids or event types). Render requests URLs are what your users will bookmark.
The component event request URLs are transitory, meaningful only while the
application is actively engaged, and not meant to be used in later
sessions.</p><div class=
"code panel pdl" style="border-width: 1px;"><div class="codeContent
panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public Object onAction(){
+ <div id="ConfluenceContent"><parameter
ac:name="style">float:right</parameter><parameter ac:name="title">Related
Articles</parameter><parameter
ac:name="class">aui-label</parameter><rich-text-body><parameter
ac:name="showLabels">false</parameter><parameter
ac:name="showSpace">false</parameter><parameter ac:name="title">Related
Articles</parameter><parameter ac:name="cql">label in
("request-processing","rendering") and space =
currentSpace()</parameter></rich-text-body><p>In essence, a Tapestry
application is a number of related pages, working together. To some degree,
each page is like an application unto itself.</p><p>Any individual request will
be targeted at a single page. Requests come in two
forms: </p><ul><li><em>component event</em> requests target a specific
component on a specific page, triggering an event within that
component</li><li><em>render</em> requests target a specific page, and stream
the HTML markup for that page back to the client</li></ul><
p>This dichotomy between component event requests and render requests
alleviates a number of problems in traditional web applications related to the
browser back button, or to the user hitting the refresh button in their
browser.</p><p><br clear="none"><span style="color: rgb(83,145,38);font-size:
20.0px;line-height: 1.5;">Logical Page Name Shortening</span></p><p>In certain
cases, Tapestry will shorten the the logical name of a page. For example, the
page class org.example.pages.address.CreateAddress will be given a logical name
of "address/Create" (the redundant "Address" is removed as a suffix). However,
this only affects how the page is referenced in URLs; the template file will
still be CreateAddress.tml, whether on the classpath, or as
address/CreateAddress.tml (in the web context).</p><p><span>Tapestry actually
creates multiple names for the name page: "address/Create" and
"address/CreateAddress" are both synonymous. You can user either in Java code
that refers to a page by n
ame, or as the page parameter of a PageLink.</span></p><h2
id="PageNavigation-ComponentEventRequests&Responses">Component Event
Requests & Responses</h2><p>Main Article: <a
href="component-events.html">Component Events</a></p><p>Component event
requests may take the form of hyperlinks (<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/EventLink.html">EventLink</a>
or <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/ActionLink.html">ActionLink</a>)
or form submissions (<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Form.html">Form</a>).</p><p>The
value returned from an <a href="component-events.html">event handler
method</a> controls the response sent to the client web browser.</p><p>The URL
for a component event request identifies the name of the page, the nested id of
the comp
onent, and the name of the event to trigger on the component (specified by the
"event" parameter of EventLink, or "action" for an ActionLink). Further, a
component event request may contain additional context information, which will
be provided to the event handler method.</p><p>These URLs expose a bit of the
internal structure of the application. Over time, as an application grows and
is maintained, the ids of components may change. This means that component
event request URLs should not be bookmarked. Fortunately, users will rarely
have the chance to do so (see below).</p><h3
id="PageNavigation-1.Nullresponse">1. Null response</h3><p>If the event handler
method returns no value, or returns null, then the current page (the page
containing the component) will render the response.</p><p>A page render URL for
the current page is created and sent to the client as a client side redirect.
The client browser will automatically submit a new request to generate the
page.</p><p>The user will
see the newly generated content in their browser. In addition, the URL in the
browser's address bar will be a render request URL. Render request URLs are
shorter and contain less application structure (for instance, they don't
include component ids or event types). Render requests URLs are what your users
will bookmark. The component event request URLs are transitory, meaningful only
while the application is actively engaged, and not meant to be used in later
sessions.</p><plain-text-body>public Object onAction(){
return null;
-}</pre>
-</div></div><h3 id="PageNavigation-2.Stringresponse">2. String
response</h3><p>When a string is returned, it is expected to be the logical
name of a page (as opposed to the page's fully qualified class name). As
elsewhere, the name of the page is case insensitive.</p><p>Again, a render
request URL will be constructed and sent to the client as a redirect.</p><div
class="code panel pdl" style="border-width: 1px;"><div class="codeContent
panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public String onAction(){
+}</plain-text-body><h3 id="PageNavigation-2.Stringresponse">2. String
response</h3><p>When a string is returned, it is expected to be the logical
name of a page (as opposed to the page's fully qualified class name). As
elsewhere, the name of the page is case insensitive.</p><p>Again, a render
request URL will be constructed and sent to the client as a
redirect.</p><plain-text-body>public String onAction(){
return "Index";
-}</pre>
-</div></div><h3 id="PageNavigation-3.Classresponse">3. Class
response</h3><p>When a class is returned, it is expected to be a page class.
Returning a page class from an event handler is safer for refactoring than
returning a page name.</p><p>As with other response types, a render request URL
will be constructed and sent to the client as a redirect.</p><div class="code
panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public Object onAction(){
+}</plain-text-body><h3 id="PageNavigation-3.Classresponse">3. Class
response</h3><p>When a class is returned, it is expected to be a page class.
Returning a page class from an event handler is safer for refactoring than
returning a page name.</p><p>As with other response types, a render request URL
will be constructed and sent to the client as a
redirect.</p><plain-text-body>public Object onAction(){
return Index.class
-}</pre>
-</div></div><h3 id="PageNavigation-4.Pageresponse">4. Page response</h3><p>You
may also return an instance of a page, rather than the name or class of a
page.</p><p>A page may be injected via the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/InjectPage.html">InjectPage</a>
annotation.</p><p>Often, you will configure the page in some way before
returning the page (examples below).</p><p>You can also return a component
within the page, but this will generate a runtime warning (unless you are doing
a partial-page update via <a href="ajax-and-zones.html">Ajax</a>).</p><div
class="code panel pdl" style="border-width: 1px;"><div class="codeContent
panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">@InjectPage
+}</plain-text-body><h3 id="PageNavigation-4.Pageresponse">4. Page
response</h3><p>You may also return an instance of a page, rather than the name
or class of a page.</p><p>A page may be injected via the <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/InjectPage.html">InjectPage</a>
annotation.</p><p>Often, you will configure the page in some way before
returning the page (examples below).</p><p>You can also return a component
within the page, but this will generate a runtime warning (unless you are doing
a partial-page update via <a
href="ajax-and-zones.html">Ajax</a>).</p><plain-text-body>@InjectPage
private Index index;
public Object onAction(){
return index;
-}</pre>
-</div></div><h3 id="PageNavigation-5.HttpError">5. HttpError</h3><p>An event
handler method may return a <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/HttpError.html">HttpError</a>
instance to send an error response to the client.</p><div class="code panel
pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public Object onAction(){
+}</plain-text-body><h3 id="PageNavigation-5.HttpError">5. HttpError</h3><p>An
event handler method may return a <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/HttpError.html">HttpError</a>
instance to send an error response to the client.</p><plain-text-body>public
Object onAction(){
return new HttpError(302, "The Error message);
-}</pre>
-</div></div><h3 id="PageNavigation-6.Linkresponse">6. Link response</h3><p>An
event handler method may return a <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/Link.html">Link</a>
instance directly. The Link is converted into a URL and a client redirect to
that URL is sent to the client.</p><p>The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ComponentResources.html">ComponentResources</a>
object that is injected into your pages (and components) has methods for
creating component links.</p><p>The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/PageRenderLinkSource.html">PageRenderLinkSource</a>
service can be injected to allow links to other pages to be created (though
that is rarely necessary, given the other options listed above).</p><h3
id="PageNavigation-7.Streamresponse">7. Stream response</h3><p>An event handler
can als
o return a <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/StreamResponse.html">StreamResponse</a>
object, which encapsulates a stream to be sent directly to the client browser.
This is useful for components that want to, say, generate an image or PDF and
provide it to the client:</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public Object onAction(){
+}</plain-text-body><h3 id="PageNavigation-6.Linkresponse">6. Link
response</h3><p>An event handler method may return a <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/Link.html">Link</a>
instance directly. The Link is converted into a URL and a client redirect to
that URL is sent to the client.</p><p>The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ComponentResources.html">ComponentResources</a>
object that is injected into your pages (and components) has methods for
creating component links.</p><p>The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/PageRenderLinkSource.html">PageRenderLinkSource</a>
service can be injected to allow links to other pages to be created (though
that is rarely necessary, given the other options listed above).</p><h3
id="PageNavigation-7.Streamresponse">7. Stream response</h3><p>An event handler
can also return a <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/StreamResponse.html">StreamResponse</a>
object, which encapsulates a stream to be sent directly to the client browser.
This is useful for components that want to, say, generate an image or PDF and
provide it to the client:</p><plain-text-body>public Object onAction(){
return new StreamResponse() {
@Override
public String getContentType() {
@@ -194,29 +95,24 @@ public Object onAction(){
response.setHeader("Content-Disposition", "attachment;
filename=\"" + myFileName + "\"");
}
};
-}</pre>
-</div></div><p> </p><h3 id="PageNavigation-8.URLresponse">8. URL
response</h3><p>A java.net.URL response is handled as a client redirect to an
external URL. (In Tapestry 5.3.x and earlier this only works for non-Ajax
requests.)</p><h3 id="PageNavigation-9.Objectresponse">9. Object
response</h3><p>Any other type of object returned from an event handler method
is an error.</p><h2 id="PageNavigation-PageRenderRequests">Page Render
Requests</h2><p>Render requests are simpler in structure and behavior than
component event requests. In the simplest case, the URL is simply the logical
name of the page.</p><p>Pages may have an <em>activation context</em>. The
activation context represents persistent information about the state of the
page. In practical terms, the activation context is usually the id of some
database-persistent object.</p><p>When a page has an activation context, the
values of the context are appended to the URL path. For example,
in <code>http://www.example.com/my
app/foo/bar</code> the "myapp" part is the servlet context (usually the name
of your app), and the "foo/bar" part is the activation context, with "foo"
being the first activation parameter and "bar" being the second.</p><p>It is
common for most pages to not have any activation context.</p><p>The activation
context may be explicitly set when the render request link is created (the
PageLink component has a context parameter for this purpose).</p><p>When no
explicit activation context is provided, the page itself is queried for its
activation context. This querying takes the form of an event trigger. The event
name is "passivate" (as we'll see shortly, there's a corresponding "activate").
The return value of the method is used as the context. For example:</p><div
class="code panel pdl" style="border-width: 1px;"><div class="codeContent
panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public class ProductDetail
+}</plain-text-body><p> </p><h3 id="PageNavigation-8.URLresponse">8. URL
response</h3><p>A java.net.URL response is handled as a client redirect to an
external URL. (In Tapestry 5.3.x and earlier this only works for non-Ajax
requests.)</p><h3 id="PageNavigation-9.Objectresponse">9. Object
response</h3><p>Any other type of object returned from an event handler method
is an error.</p><h2 id="PageNavigation-PageRenderRequests">Page Render
Requests</h2><p>Render requests are simpler in structure and behavior than
component event requests. In the simplest case, the URL is simply the logical
name of the page.</p><p>Pages may have an <em>activation context</em>. The
activation context represents persistent information about the state of the
page. In practical terms, the activation context is usually the id of some
database-persistent object.</p><p>When a page has an activation context, the
values of the context are appended to the URL path. For example,
in <code>http://www.example
.com/myapp/foo/bar</code> the "myapp" part is the servlet context (usually the
name of your app), and the "foo/bar" part is the activation context, with "foo"
being the first activation parameter and "bar" being the second.</p><p>It is
common for most pages to not have any activation context.</p><p>The activation
context may be explicitly set when the render request link is created (the
PageLink component has a context parameter for this purpose).</p><p>When no
explicit activation context is provided, the page itself is queried for its
activation context. This querying takes the form of an event trigger. The event
name is "passivate" (as we'll see shortly, there's a corresponding "activate").
The return value of the method is used as the context. For
example:</p><parameter ac:name="">java</parameter><plain-text-body>public class
ProductDetail
{
private Product product;
. . .
long onPassivate() { return product.getId(); }
}
-</pre>
-</div></div><p>The activation context may consist of a series of values, in
which case the return value of the method should be an array or a List.</p><div
class="confluence-information-macro
confluence-information-macro-information"><span class="aui-icon aui-icon-small
aui-iconfont-info confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>Note: If you are using the <a
href="hibernate-user-guide.html">tapestry-hibernate</a> integration library and
your passivate context is a Hibernate entity, then you can just use the entity
itself, not its id. Tapestry will automatically extract the entity's id into
the URL, and convert it back for the "activate" event handler
method.</p></div></div><h2 id="PageNavigation-Pageactivation">Page
activation</h2><p>When a page render request arrives, the page is
<em>activated</em> before it is rendered.</p><div class="navmenu"
style="float:right; background:#eee; margin:3px; padding:0 1em">
-<p> <strong>JumpStart Demos:</strong><br clear="none">
- <a class="external-link"
href="http://jumpstart.doublenegative.com.au/jumpstart/examples/navigation/onactivateandonpassivate/3"
rel="nofollow">onActivate and onPassivate</a><br clear="none">
- <a class="external-link"
href="http://jumpstart.doublenegative.com.au/jumpstart/examples/infrastructure/handlingabadcontext/1"
rel="nofollow">Handling A Bad Context</a></p></div>Activation serves two
purposes:<ul><li>It allows the page to restore its internal state from data
encoded into the URL (the activation context discussed above).</li><li>It
provides coarse approach to validating access to the page.</li></ul><p>The
later case – validation – is generally concerned with user
identity and access; if you have pages that may only be accessed by certain
users, you may use the page's activate event handler for verifying that
access.</p><p>Page activation uses Tapestry's <em>Component Event</em>
mechanism. See <a href="component-events.html">Component Events</a> for
details.</p><p>A page's activate event handler mirrors its passivate
handler:</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> private Product product;
+</plain-text-body><p>The activation context may consist of a series of values,
in which case the return value of the method should be an array or a
List.</p><rich-text-body><p>Note: If you are using the <a
href="hibernate-user-guide.html">tapestry-hibernate</a> integration library and
your passivate context is a Hibernate entity, then you can just use the entity
itself, not its id. Tapestry will automatically extract the entity's id into
the URL, and convert it back for the "activate" event handler
method.</p></rich-text-body><h2 id="PageNavigation-Pageactivation">Page
activation</h2><p>When a page render request arrives, the page is
<em>activated</em> before it is
rendered.<plain-text-body>{float:right|background=#eee|padding=0 1em}
+ *JumpStart Demos:*
+ [onActivate and
onPassivate|http://jumpstart.doublenegative.com.au/jumpstart/examples/navigation/onactivateandonpassivate/3]
+ [Handling A Bad
Context|http://jumpstart.doublenegative.com.au/jumpstart/examples/infrastructure/handlingabadcontext/1]
+{float}</plain-text-body>Activation serves two purposes:</p><ul><li>It allows
the page to restore its internal state from data encoded into the URL (the
activation context discussed above).</li><li>It provides coarse approach to
validating access to the page.</li></ul><p>The later case –
validation – is generally concerned with user identity and access;
if you have pages that may only be accessed by certain users, you may use the
page's activate event handler for verifying that access.</p><p>Page activation
uses Tapestry's <em>Component Event</em> mechanism. See <a
href="component-events.html">Component Events</a> for details.</p><p>A page's
activate event handler mirrors its passivate handler:</p><parameter
ac:name="">java</parameter><plain-text-body> private Product product;
. . .
void onActivate(long productId)
{
product = productDAO.getById(productId);
}
. . .
-</pre>
-</div></div><p>Here's the relevant part: when the page renders, it is likely
to include more component event request URLs (links and forms). The component
event requests for those links and forms will <em>also</em> start by activating
the page, before performing other work. This forms an unbroken chain of
requests that include the same activation context.</p><p>To some degree, this
same effect could be accomplished using a <a
href="persistent-page-data.html">persistent page value</a>, but that requires
an active servlet session, and the result is not bookmarkable.</p><p>Your
activate event handler, like any event handler, may also return a value, which
is treated identically to a return value of a component event method. This
technique is commonly used as a simple access validation mechanism.</p><p>You
sometimes need to handle multiple page activation scenarios in one page class.
You could create multiple activate event handler methods with different
arguments (see the "Multiple Me
thod Matches" section at <a href="component-events.html">Component Events</a>
for details), but if you do so, you should generally
return <code>true</code> from each to avoid having more than one
activation event handler method from being called for each page request.
However, a better approach is to create one method with an EventContext
argument. Tapestry will populate the EventContext argument with all of the
activation parameters, and the EventContext's <code>get</code> method will
retrieve and coerce each parameter to the desired type. For example:</p><div
class="code panel pdl" style="border-width: 1px;"><div class="codeContent
panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> . . .
+</plain-text-body><p>Here's the relevant part: when the page renders, it is
likely to include more component event request URLs (links and forms). The
component event requests for those links and forms will <em>also</em> start by
activating the page, before performing other work. This forms an unbroken chain
of requests that include the same activation context.</p><p>To some degree,
this same effect could be accomplished using a <a
href="persistent-page-data.html">persistent page value</a>, but that requires
an active servlet session, and the result is not bookmarkable.</p><p>Your
activate event handler, like any event handler, may also return a value, which
is treated identically to a return value of a component event method. This
technique is commonly used as a simple access validation mechanism.</p><p>You
sometimes need to handle multiple page activation scenarios in one page class.
You could create multiple activate event handler methods with different
arguments (see the "Multi
ple Method Matches" section at <a href="component-events.html">Component
Events</a> for details), but if you do so, you should generally
return <code>true</code> from each to avoid having more than one
activation event handler method from being called for each page request.
However, a better approach is to create one method with an EventContext
argument. Tapestry will populate the EventContext argument with all of the
activation parameters, and the EventContext's <code>get</code> method will
retrieve and coerce each parameter to the desired type. For
example:</p><parameter ac:name="">java</parameter><plain-text-body> . . .
void onActivate(EventContext eventContext) {
@@ -235,14 +131,10 @@ public Object onAction(){
}
. . .
-</pre>
-</div></div><h2 id="PageNavigation-PageNavigationPatterns">Page Navigation
Patterns</h2><p>This combination of action links and context and page context
can be put together in any number of ways.</p><p>Let's take a typical
master/detail relationship using the concept of a product catalog page. In this
example, the ProductListing page is a list of products, and the ProductDetails
page must display the details for a specific product.</p><h3
id="PageNavigation-Pattern1:Componenteventrequests/PersistentData">Pattern 1:
Component event requests / Persistent Data</h3><p>In this pattern, the
ProductListing page uses action events and a persistent field on the
ProductDetails page.</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>ProductListing.html</b></div><div class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> <t:loop source="products" value="product">
+</plain-text-body><h2 id="PageNavigation-PageNavigationPatterns">Page
Navigation Patterns</h2><p>This combination of action links and context and
page context can be put together in any number of ways.</p><p>Let's take a
typical master/detail relationship using the concept of a product catalog page.
In this example, the ProductListing page is a list of products, and the
ProductDetails page must display the details for a specific product.</p><h3
id="PageNavigation-Pattern1:Componenteventrequests/PersistentData">Pattern 1:
Component event requests / Persistent Data</h3><p>In this pattern, the
ProductListing page uses action events and a persistent field on the
ProductDetails page.</p><parameter ac:name="">java</parameter><parameter
ac:name="title">ProductListing.html</parameter><plain-text-body> <t:loop
source="products" value="product">
<a t:type="actionlink" t:id="select"
context="product.id">${product.name}</a>
</t:loop>
-</pre>
-</div></div><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>ProductListing.java</b></div><div class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @InjectPage
+</plain-text-body><parameter ac:name="">java</parameter><parameter
ac:name="title">ProductListing.java</parameter><plain-text-body> @InjectPage
private ProductDetails details;
Object onActionFromSelect(long productId)
@@ -251,9 +143,7 @@ public Object onAction(){
return details;
}
-</pre>
-</div></div><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>ProductDetails.java</b></div><div class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Inject
+</plain-text-body><parameter ac:name="">java</parameter><parameter
ac:name="title">ProductDetails.java</parameter><plain-text-body> @Inject
private ProductDAO dao;
private Product product;
@@ -267,9 +157,7 @@ public Object onAction(){
{
product = dao.getById(productId);
}
-</pre>
-</div></div><p>This is a minimal approach, perhaps good enough for a
prototype.</p><p>When the user clicks a link, the component event request URL
will initially be something like "http://.../productlisting.select/99" and the
final render request URL will be something like "http://.../productdetails".
Notice that the product id ("99") does not appear in the render request
URL.</p><p>This pattern has the following drawbacks:</p><ul><li>It requires a
session (to store the productId field between requests).</li><li>It may fail if
the ProductDetails page is accessed before a valid product id is
set.</li><li>The URL does not indicate the identity of the product; if the user
bookmarks the URL and comes back later, they will trigger the previous case (no
valid product id).</li></ul><p><span class="confluence-anchor-link"
id="PageNavigation-activationpattern"></span></p><h3
id="PageNavigation-Pattern2:ComponentEventRequests/NoPersistentData">Pattern 2:
Component Event Requests / No Persiste
nt Data</h3><p>We can improve the previous example without changing the
ProductListing page, using a passivation and activation context to avoid the
session and make the links more bookmarkable.</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>ProductDetails.java</b></div><div
class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Inject
+</plain-text-body><p>This is a minimal approach, perhaps good enough for a
prototype.</p><p>When the user clicks a link, the component event request URL
will initially be something like "http://.../productlisting.select/99" and the
final render request URL will be something like "http://.../productdetails".
Notice that the product id ("99") does not appear in the render request
URL.</p><p>This pattern has the following drawbacks:</p><ul><li>It requires a
session (to store the productId field between requests).</li><li>It may fail if
the ProductDetails page is accessed before a valid product id is
set.</li><li>The URL does not indicate the identity of the product; if the user
bookmarks the URL and comes back later, they will trigger the previous case (no
valid product id).</li></ul><p><parameter
ac:name="">activationpattern</parameter></p><h3
id="PageNavigation-Pattern2:ComponentEventRequests/NoPersistentData">Pattern 2:
Component Event Requests / No Persistent Data</h3><p>We can imp
rove the previous example without changing the ProductListing page, using a
passivation and activation context to avoid the session and make the links more
bookmarkable.</p><parameter ac:name="">java</parameter><parameter
ac:name="title">ProductDetails.java</parameter><plain-text-body> @Inject
private ProductDAO dao;
private Product product;
@@ -286,17 +174,11 @@ public Object onAction(){
}
long onPassivate() { return productId; }
-</pre>
-</div></div><p>This change ensures that the render request URL will include
the product id, i.e., "http://.../productdetails/99".</p><p>It has the
advantage that the connection from page to page occurs in type-safe Java code,
inside the onActionFromSelect method of ProductListing. It has the disadvantage
that clicking a link requires two round trips to the server.</p><h3
id="PageNavigation-Pattern3:RenderRequestsOnly">Pattern 3: Render Requests
Only</h3><p>This is the most common version of this master/detail
relationship.</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>ProductListing.html</b></div><div class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> <t:loop source="products" value="product">
+</plain-text-body><p>This change ensures that the render request URL will
include the product id, i.e., "http://.../productdetails/99".</p><p>It has the
advantage that the connection from page to page occurs in type-safe Java code,
inside the onActionFromSelect method of ProductListing. It has the disadvantage
that clicking a link requires two round trips to the server.</p><h3
id="PageNavigation-Pattern3:RenderRequestsOnly">Pattern 3: Render Requests
Only</h3><p>This is the most common version of this master/detail
relationship.</p><parameter ac:name="">java</parameter><parameter
ac:name="title">ProductListing.html</parameter><plain-text-body> <t:loop
source="products" value="product">
<a t:type="pagelink" page="productdetails"
context="product.id">${product.name}</a>
</t:loop>
-</pre>
-</div></div><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>ProductListing.java</b></div><div class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">No code is needed to support the link.
-</pre>
-</div></div><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>ProductDetails.java</b></div><div class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Inject
+</plain-text-body><parameter ac:name="">java</parameter><parameter
ac:name="title">ProductListing.java</parameter><plain-text-body>No code is
needed to support the link.
+</plain-text-body><parameter ac:name="">java</parameter><parameter
ac:name="title">ProductDetails.java</parameter><plain-text-body> @Inject
private ProductDAO dao;
private Product product;
@@ -311,8 +193,7 @@ public Object onAction(){
}
long onPassivate() { return productId; }
-</pre>
-</div></div><p>The setProductId() method is no longer needed.</p><h3
id="PageNavigation-Limitations">Limitations</h3><p>As your application's
workflow expands, you may find that there is not a reasonable way to avoid
storing some data persistently between requests, outside of the page activation
context. For example, if from the ProductDetails page, the user is allowed to
navigate to related pages and then back to ProductDetails, it starts to become
necessary to keep passing that product id around from page to page to
page.</p><p>At some point, persistent values make more sense. Tapestry has
several persistence strategies available, including one that stores data in URL
query parameters. See <a href="persistent-page-data.html">Persistent Page
Data</a> for details.</p></div>
+</plain-text-body><p>The setProductId() method is no longer needed.</p><h3
id="PageNavigation-Limitations">Limitations</h3><p>As your application's
workflow expands, you may find that there is not a reasonable way to avoid
storing some data persistently between requests, outside of the page activation
context. For example, if from the ProductDetails page, the user is allowed to
navigate to related pages and then back to ProductDetails, it starts to become
necessary to keep passing that product id around from page to page to
page.</p><p>At some point, persistent values make more sense. Tapestry has
several persistence strategies available, including one that stores data in URL
query parameters. See <a href="persistent-page-data.html">Persistent Page
Data</a> for details.</p></div>
</div>
<div class="clearer"></div>
Modified: websites/production/tapestry/content/persistent-page-data.html
==============================================================================
--- websites/production/tapestry/content/persistent-page-data.html (original)
+++ websites/production/tapestry/content/persistent-page-data.html Tue Sep 26
19:20:27 2017
@@ -27,16 +27,6 @@
</title>
<link type="text/css" rel="stylesheet" href="/resources/space.css" />
- <link href='/resources/highlighter/styles/shCoreCXF.css'
rel='stylesheet' type='text/css' />
- <link href='/resources/highlighter/styles/shThemeCXF.css' rel='stylesheet'
type='text/css' />
- <script src='/resources/highlighter/scripts/shCore.js'
type='text/javascript'></script>
- <script src='/resources/highlighter/scripts/shBrushJava.js'
type='text/javascript'></script>
- <script src='/resources/highlighter/scripts/shBrushXml.js'
type='text/javascript'></script>
- <script src='/resources/highlighter/scripts/shBrushPlain.js'
type='text/javascript'></script>
- <script>
- SyntaxHighlighter.defaults['toolbar'] = false;
- SyntaxHighlighter.all();
- </script>
<link href="/styles/style.css" rel="stylesheet" type="text/css"/>
@@ -77,106 +67,23 @@
</div>
<div id="content">
- <div id="ConfluenceContent"><div
class="confluence-information-macro
confluence-information-macro-information"><span class="aui-icon aui-icon-small
aui-iconfont-info confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>The use of the term "persistence"
here refers to <em>page-level</em> persistence, NOT database
persistence.</p></div></div><p>Most instance variables in Tapestry are
automatically cleared at the end of each request. This is important, as it
pertains to how Tapestry pages are shared, over time, by many users.</p><div
class="aui-label" style="float:right" title="Related Articles">
-
-
-
-
-
-
-
-
-<h3>Related Articles</h3>
-
-<ul class="content-by-label"><li>
- <div>
- <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
-
- <div class="details">
- <a href="performance-and-clustering.html">Performance
and Clustering</a>
-
-
- </div>
- </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
-
- <div class="details">
- <a href="session-storage.html">Session Storage</a>
-
-
- </div>
- </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
-
- <div class="details">
- <a href="persistent-page-data.html">Persistent Page
Data</a>
-
-
- </div>
- </li></ul>
-</div>
-
-
-<p>However, you often want to store some data on a <em>single</em> page, and
have access to it in later requests to that same page, without having to store
it in a database between requests. (To store values across multiple pages, see
<a href="session-storage.html">Session Storage</a>.)</p><p>Making page data
persist across requests to a single page is accomplished with the @<a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Persist.html">Persist</a>
annotation. This annotation is applied to private instance fields of
components:</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist
+ <div id="ConfluenceContent"><rich-text-body><p>The use of the
term "persistence" here refers to <em>page-level</em> persistence, NOT database
persistence.</p></rich-text-body><p>Most instance variables in Tapestry are
automatically cleared at the end of each request. This is important, as it
pertains to how Tapestry pages are shared, over time, by many
users.</p><parameter ac:name="style">float:right</parameter><parameter
ac:name="title">Related Articles</parameter><parameter
ac:name="class">aui-label</parameter><rich-text-body><parameter
ac:name="showLabels">false</parameter><parameter
ac:name="showSpace">false</parameter><parameter ac:name="title">Related
Articles</parameter><parameter ac:name="cql">label = "persistence" and space =
currentSpace()</parameter></rich-text-body><p>However, you often want to store
some data on a <em>single</em> page, and have access to it in later requests to
that same page, without having to store it in a database between requests. (T
o store values across multiple pages, see <a
href="session-storage.html">Session Storage</a>.)</p><p>Making page data
persist across requests to a single page is accomplished with the @<a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Persist.html">Persist</a>
annotation. This annotation is applied to private instance fields of
components:</p><parameter ac:name="">java</parameter><plain-text-body> @Persist
private int value;
-</pre>
-</div></div><p>Such annotated fields will retain their state between requests.
Generally, speaking, this means that the value is stored into the session (but
other approaches are possible).</p><p>Whenever you make a change to a
persistent field, its value is saved. On later requests to the same page, the
value for the field is restored.</p><h2
id="PersistentPageData-PersistenceStrategies">Persistence Strategies</h2><p>The
value for each field is the <em>strategy</em> used to store the field between
requests.</p><h3 id="PersistentPageData-SessionStrategy">Session
Strategy</h3><p></p><div class="navmenu" style="float:right; background:#eee;
margin:3px; padding:0 1em">
-<p> <strong>JumpStart Demo:</strong><br clear="none">
- <a class="external-link"
href="http://jumpstart.doublenegative.com.au/jumpstart/examples/state/storingdatainapage"
rel="nofollow">Storing Data in a Page</a><br clear="none">
- <a class="external-link"
href="http://jumpstart.doublenegative.com.au/jumpstart/examples/state/passingdatabetweenpages"
rel="nofollow">Passing Data Between Pages</a></p></div>The session strategy
stores field changes into the session; the session is created as necessary.
Session strategy is the default strategy used unless otherwise overridden.<p>A
suitably long session attribute name is used; it incorporates the name of the
page, the nested component id, and the name of the field.</p><div class="code
panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>Example: Session Strategy</b></div><div
class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist
+</plain-text-body><p>Such annotated fields will retain their state between
requests. Generally, speaking, this means that the value is stored into the
session (but other approaches are possible).</p><p>Whenever you make a change
to a persistent field, its value is saved. On later requests to the same page,
the value for the field is restored.</p><h2
id="PersistentPageData-PersistenceStrategies">Persistence Strategies</h2><p>The
value for each field is the <em>strategy</em> used to store the field between
requests.</p><h3 id="PersistentPageData-SessionStrategy">Session
Strategy</h3><p><plain-text-body>{float:right|background=#eee|padding=0 1em}
+ *JumpStart Demo:*
+ [Storing Data in a
Page|http://jumpstart.doublenegative.com.au/jumpstart/examples/state/storingdatainapage]
+ [Passing Data Between
Pages|http://jumpstart.doublenegative.com.au/jumpstart/examples/state/passingdatabetweenpages]
+{float}</plain-text-body>The session strategy stores field changes into the
session; the session is created as necessary. Session strategy is the default
strategy used unless otherwise overridden.</p><p>A suitably long session
attribute name is used; it incorporates the name of the page, the nested
component id, and the name of the field.</p><parameter ac:name="title">Example:
Session Strategy</parameter><plain-text-body> @Persist
private int value;
-</pre>
-</div></div><h3 id="PersistentPageData-FlashStrategy">Flash
Strategy</h3><p>The flash strategy stores information in the session as well,
just for not very long. Values are stored into the session, but then deleted
from the session as they are first used to restore a page's state.</p><p>The
flash is typically used to store temporary messages that should only be
displayed to the user once.</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>Example: Flash Strategy</b></div><div class="codeContent panelContent
pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist(PersistenceConstants.FLASH)
+</plain-text-body><h3 id="PersistentPageData-FlashStrategy">Flash
Strategy</h3><p>The flash strategy stores information in the session as well,
just for not very long. Values are stored into the session, but then deleted
from the session as they are first used to restore a page's state.</p><p>The
flash is typically used to store temporary messages that should only be
displayed to the user once.</p><parameter ac:name="title">Example: Flash
Strategy</parameter><plain-text-body> @Persist(PersistenceConstants.FLASH)
private int value;
-</pre>
-</div></div><h3 id="PersistentPageData-ClientStrategy">Client
Strategy</h3><p>The field is persisted onto the client; you will see an
additional query parameter in each URL (or an extra hidden field in each
form).</p><p>Client persistence is somewhat expensive. It can bloat the size of
the rendered pages by adding hundreds of characters to each link. There is
extra processing on each request to de-serialize the values encoded into the
query parameter.</p><p>Client persistence does not scale very well; as more
information is stored into the query parameter, its length can become
problematic. In many cases, web browsers, firewalls or other servers may
silently truncate the URL which will break the application.</p><p>Use client
persistence with care, and store a minimal amount of data. Try to store the
identity (that is, primary key) of an object, rather than the object
itself.</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="bord
er-bottom-width: 1px;"><b>Example: Client Strategy</b></div><div
class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist(PersistenceConstants.CLIENT)
+</plain-text-body><h3 id="PersistentPageData-ClientStrategy">Client
Strategy</h3><p>The field is persisted onto the client; you will see an
additional query parameter in each URL (or an extra hidden field in each
form).</p><p>Client persistence is somewhat expensive. It can bloat the size of
the rendered pages by adding hundreds of characters to each link. There is
extra processing on each request to de-serialize the values encoded into the
query parameter.</p><p>Client persistence does not scale very well; as more
information is stored into the query parameter, its length can become
problematic. In many cases, web browsers, firewalls or other servers may
silently truncate the URL which will break the application.</p><p>Use client
persistence with care, and store a minimal amount of data. Try to store the
identity (that is, primary key) of an object, rather than the object
itself.</p><parameter ac:name="title">Example: Client
Strategy</parameter><plain-text-body> @Persist(Persisten
ceConstants.CLIENT)
private int value;
-</pre>
-</div></div><h3 id="PersistentPageData-HibernateEntityStrategy">Hibernate
Entity Strategy</h3><p><span style="line-height: 1.4285715;">Entity persistence
is provided by the tapestry-hibernate module (which extends Tapestry with new
features).</span></p><p>In Entity persistence, the field should store a
Hibernate entity instance.</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>"Hibernate Entity Strategy"</b></div><div class="codeContent
panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist(HibernatePersistenceConstants.ENTITY)
- private User user;</pre>
-</div></div><p> </p><p>The value stored in the HttpSession is
a <em>token</em> for the entity: its Java class name and primary key. When
the field is restored in a later request, the entity is re-instantiated using
that data.</p><p>What is <em>not</em> stored is any changes to the
persistent entity that are not committed to the external datastore (the
database).</p><p>Starting in Tapestry 5.4, it is possible to store a
non-persistent entity (a transient entity). A transient entity is stored
directly into the HttpSession, and should be Serializable if the application is
clustered.</p><h3 id="PersistentPageData-JPAEntityStrategy">JPA Entity
Strategy</h3><p>The tapestry-jpa module uses a similar strategy. However, at
the current time it can only store a persisted entity (one that has been saved
to the database and has a primary key).</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>"Exa
mple: JPA Entity Strategy"</b></div><div class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist(JpaPersistenceConstants.ENTITY)
- private Account account;</pre>
-</div></div><p><span style="color: rgb(83,145,38);font-size:
20.0px;line-height: 1.5;">Persistence Strategy Inheritance</span></p><p>By
default the value for the Persist annotation is the empty string. When this is
true, then the actual strategy to be used is determined by a search up the
component hierarchy.</p><p>For each component, the meta-data property
<code>tapestry.persistence-strategy</code> is checked. This can be specified
using the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Meta.html">Meta</a>
annotation.</p><p>If the value is non-blank, then that strategy is used. This
allows a component to control the persistence strategy used inside any
sub-components (that don't explicitly use a different strategy).</p><p>In any
case, if no component provides the meta data, then the ultimate default,
"session", is used.</p><h2 id="PersistentPageData-DefaultValues">Default
Values</h2><p>Fields marked with @Persist may not
have default values (whether set inline, or inside a constructor).</p><h2
id="PersistentPageData-ClearingPersistentFields">Clearing Persistent
Fields</h2><p>If you reach a point where you know that all data for a page can
be discarded, you can do exactly that.</p><p>The method
<code>discardPersistentFieldChanges()</code> of ComponentResources will discard
all persistent fields for the page, regardless of which strategy is used to
store the property. This will not affect the page in memory, but takes effect
for subsequent requests.</p><p></p><h2
id="PersistentPageData-ClusteringIssues">Clustering Issues</h2>
-
-<p>The Servlet API was designed with the intention that there would be only a
modest amount of server-side state, and that the stored values would be
individual numbers and strings, and thus, immutable.</p>
-
-<p>However, many web applications do not use the HttpSession this way, instead
storing large, mutable objects in the session. This is not a problem for single
servers, but in a cluster, anything stored in the session must be serialized to
a bytestream and distributed to other servers within the cluster, and restored
there.</p>
-
-<p>Most application servers perform that serialization and distribution
whenever HttpSession.setAttribute() is called. This creates a data consistency
problem for mutable objects, because if you read a mutable session object,
change its state, but <em>don't</em> invoke setAttribute(), the changes will be
isolated to just a single server in the cluster.</p>
-
-<p>Tapestry attempts to solve this: any session-persisted object that is read
during a request will be re-stored back into the HttpSession at the end of the
request. This ensures that changed internal state of those mutable objects is
properly replicated around the cluster.</p>
-
-<p>But while this solution solves the data consistency problem, it does so at
the expense of performance, since all of those calls to setAttribute() result
in extra session data being replicated needlessly if the internal state of the
mutable object hasn't changed.</p>
-
-<p>Tapestry has solutions to this, too:</p>
-
-<h3
id="PersistentPageData-@ImmutableSessionPersistedObjectAnnotation">@ImmutableSessionPersistedObject
Annotation</h3>
-
-<p>Tapestry knows that Java's String, Number and Boolean classes are
immutable. Immutable objects do not require a re-store into the session.</p>
-
-<p>You can mark your own session objects as immutable (and thus not requiring
session replication) using the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/ImmutableSessionPersistedObject.html">ImmutableSessionPersistedObject</a>
annotation.</p>
-
-<h3
id="PersistentPageData-OptimizedSessionPersistedObjectInterface">OptimizedSessionPersistedObject
Interface</h3>
-
-<p>The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/OptimizedSessionPersistedObject">OptimizedSessionPersistedObject</a>
interface allows an object to control this behavior. An object with this
interface can track when its mutable state changes. Typically, you should
extend from the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.html">BaseOptimizedSessionPersistedObject</a>
base class.</p>
-
-<h3
id="PersistentPageData-SessionPersistedObjectAnalyzerService">SessionPersistedObjectAnalyzer
Service</h3>
-
-<p>The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.html">SessionPersistedObjectAnalyzer</a>
service is ultimately responsible for determining whether a session persisted
object is dirty or not (dirty meaning in need of a restore into the session).
This is an extensible service where new strategies, for new classes, can be
introduced.</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>Example: Entity Session Strategy</b></div><div class="codeContent
panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist(HibernatePersistenceConstants.ENTITY)
- private User user;</pre>
-</div></div><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>"Example:JAP Session Strategy"</b></div><div class="codeContent
panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist(JpaPersistenceConstants.ENTITY)
- private Account account;</pre>
-</div></div></div>
+</plain-text-body><h3
id="PersistentPageData-HibernateEntityStrategy">Hibernate Entity
Strategy</h3><p><span style="line-height: 1.4285715;">Entity persistence is
provided by the tapestry-hibernate module (which extends Tapestry with new
features).</span></p><p>In Entity persistence, the field should store a
Hibernate entity instance.</p><parameter ac:name="title">"Hibernate Entity
Strategy"</parameter><plain-text-body>
@Persist(HibernatePersistenceConstants.ENTITY)
+ private User user;</plain-text-body><p> </p><p>The value stored in the
HttpSession is a <em>token</em> for the entity: its Java class name and
primary key. When the field is restored in a later request, the entity is
re-instantiated using that data.</p><p>What is <em>not</em> stored is any
changes to the persistent entity that are not committed to the external
datastore (the database).</p><p>Starting in Tapestry 5.4, it is possible to
store a non-persistent entity (a transient entity). A transient entity is
stored directly into the HttpSession, and should be Serializable if the
application is clustered.</p><h3 id="PersistentPageData-JPAEntityStrategy">JPA
Entity Strategy</h3><p>The tapestry-jpa module uses a similar strategy.
However, at the current time it can only store a persisted entity (one that has
been saved to the database and has a primary key).</p><parameter
ac:name="title">"Example: JPA Entity Strategy"</parameter><plain-text-body>
@Persist(JpaPersistenc
eConstants.ENTITY)
+ private Account account;</plain-text-body><p><span style="color:
rgb(83,145,38);font-size: 20.0px;line-height: 1.5;">Persistence Strategy
Inheritance</span></p><p>By default the value for the Persist annotation is the
empty string. When this is true, then the actual strategy to be used is
determined by a search up the component hierarchy.</p><p>For each component,
the meta-data property <code>tapestry.persistence-strategy</code> is checked.
This can be specified using the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Meta.html">Meta</a>
annotation.</p><p>If the value is non-blank, then that strategy is used. This
allows a component to control the persistence strategy used inside any
sub-components (that don't explicitly use a different strategy).</p><p>In any
case, if no component provides the meta data, then the ultimate default,
"session", is used.</p><h2 id="PersistentPageData-DefaultValues">Default
Values</h2><p>Fie
lds marked with @Persist may not have default values (whether set inline, or
inside a constructor).</p><h2
id="PersistentPageData-ClearingPersistentFields">Clearing Persistent
Fields</h2><p>If you reach a point where you know that all data for a page can
be discarded, you can do exactly that.</p><p>The method
<code>discardPersistentFieldChanges()</code> of ComponentResources will discard
all persistent fields for the page, regardless of which strategy is used to
store the property. This will not affect the page in memory, but takes effect
for subsequent requests.</p><p><parameter ac:name=""><a
href="clustering-issues.html">Clustering Issues</a></parameter></p><parameter
ac:name="title">Example: Entity Session Strategy</parameter><plain-text-body>
@Persist(HibernatePersistenceConstants.ENTITY)
+ private User user;</plain-text-body><parameter ac:name="title">"Example:JAP
Session Strategy"</parameter><plain-text-body>
@Persist(JpaPersistenceConstants.ENTITY)
+ private Account account;</plain-text-body></div>
</div>
<div class="clearer"></div>
Modified: websites/production/tapestry/content/release-notes-50.html
==============================================================================
--- websites/production/tapestry/content/release-notes-50.html (original)
+++ websites/production/tapestry/content/release-notes-50.html Tue Sep 26
19:20:27 2017
@@ -27,16 +27,6 @@
</title>
<link type="text/css" rel="stylesheet" href="/resources/space.css" />
- <link href='/resources/highlighter/styles/shCoreCXF.css'
rel='stylesheet' type='text/css' />
- <link href='/resources/highlighter/styles/shThemeCXF.css' rel='stylesheet'
type='text/css' />
- <script src='/resources/highlighter/scripts/shCore.js'
type='text/javascript'></script>
- <script src='/resources/highlighter/scripts/shBrushJava.js'
type='text/javascript'></script>
- <script src='/resources/highlighter/scripts/shBrushXml.js'
type='text/javascript'></script>
- <script src='/resources/highlighter/scripts/shBrushPlain.js'
type='text/javascript'></script>
- <script>
- SyntaxHighlighter.defaults['toolbar'] = false;
- SyntaxHighlighter.all();
- </script>
<link href="/styles/style.css" rel="stylesheet" type="text/css"/>
@@ -77,19 +67,12 @@
</div>
<div id="content">
- <div id="ConfluenceContent">
+ <div
id="ConfluenceContent"><plain-text-body>{scrollbar}</plain-text-body>
<p>This is the consolidated list of changes between Tapestry versions 5.0.3
and 5.0.19. Before upgrading, be sure to review the <a
href="how-to-upgrade.html">How to Upgrade</a> instructions.</p>
<p><strong>Contents</strong></p>
-<style type="text/css">/*<![CDATA[*/
-div.rbtoc1499639566337 {padding: 0px;}
-div.rbtoc1499639566337 ul {list-style: disc;margin-left: 0px;padding-left:
5px;}
-div.rbtoc1499639566337 li {margin-left: 0px;padding-left: 0px;}
-
-/*]]>*/</style><div class="toc-macro rbtoc1499639566337">
-<ul class="toc-indentation"><li><a
href="#ReleaseNotes5.0-TapestryVersion5.0.19">Tapestry Version
5.0.19</a></li><li><a href="#ReleaseNotes5.0-TapestryVersion5.0.18">Tapestry
Version 5.0.18</a></li><li><a
href="#ReleaseNotes5.0-TapestryVersion5.0.17">Tapestry Version
5.0.17</a></li><li><a href="#ReleaseNotes5.0-TapestryVersion5.0.16">Tapestry
Version 5.0.16</a></li><li><a
href="#ReleaseNotes5.0-TapestryVersion5.0.15">Tapestry Version
5.0.15</a></li><li><a href="#ReleaseNotes5.0-TapestryVersion5.0.14">Tapestry
Version 5.0.14</a></li><li><a
href="#ReleaseNotes5.0-TapestryVersion5.0.13">Tapestry Version
5.0.13</a></li><li><a href="#ReleaseNotes5.0-TapestryVersion5.0.12">Tapestry
Version 5.0.12</a></li><li><a
href="#ReleaseNotes5.0-TapestryVersion5.0.11">Tapestry Version
5.0.11</a></li><li><a href="#ReleaseNotes5.0-TapestryVersion5.0.10">Tapestry
Version 5.0.10</a></li><li><a
href="#ReleaseNotes5.0-TapestryVersion5.0.9">Tapestry Version
5.0.9</a></li><li><a href="#ReleaseNot
es5.0-TapestryVersion5.0.8">Tapestry Version 5.0.8</a></li><li><a
href="#ReleaseNotes5.0-TapestryVersion5.0.7">Tapestry Version
5.0.7</a></li><li><a href="#ReleaseNotes5.0-TapestryVersion5.0.6">Tapestry
Version 5.0.6</a></li><li><a
href="#ReleaseNotes5.0-TapestryVersion5.0.5">Tapestry Version
5.0.5</a></li><li><a href="#ReleaseNotes5.0-TapestryVersion5.0.4">Tapestry
Version 5.0.4</a></li><li><a
href="#ReleaseNotes5.0-TapestryVersion5.0.3">Tapestry Version
5.0.3</a></li></ul>
-</div>
+<parameter ac:name="maxLevel">2</parameter><parameter
ac:name="minLevel">2</parameter><parameter ac:name="indent">5px</parameter>
<h2 id="ReleaseNotes5.0-TapestryVersion5.0.19">Tapestry Version 5.0.19</h2>
@@ -431,7 +414,8 @@ div.rbtoc1499639566337 li {margin-left:
<ul><li><a class="external-link"
href="https://issues.apache.org/jira/browse/TAPESTRY-1276">TAPESTRY-1276</a>
– If component should include an optional negate parameter</li><li><a
class="external-link"
href="https://issues.apache.org/jira/browse/TAPESTRY-1284">TAPESTRY-1284</a>
– Tapestry Spring integration module</li><li><a class="external-link"
href="https://issues.apache.org/jira/browse/TAPESTRY-1292">TAPESTRY-1292</a>
– Allow lists to be used as select models</li><li><a
class="external-link"
href="https://issues.apache.org/jira/browse/TAPESTRY-1302">TAPESTRY-1302</a>
– JavaScript support</li><li><a class="external-link"
href="https://issues.apache.org/jira/browse/TAPESTRY-1311">TAPESTRY-1311</a>
– Identify type of component via tag element name in templates</li><li><a
class="external-link"
href="https://issues.apache.org/jira/browse/TAPESTRY-1319">TAPESTRY-1319</a>
– tapestry.InfrastructureOverrides is not yet implemented</li><li><a cla
ss="external-link"
href="https://issues.apache.org/jira/browse/TAPESTRY-1325">TAPESTRY-1325</a>
– Add an "asset:" object provider, to simplfy injecting assets into
services</li><li><a class="external-link"
href="https://issues.apache.org/jira/browse/TAPESTRY-1341">TAPESTRY-1341</a>
– Allow service builders named "build" and determine service id from the
result type</li></ul>
-</div>
+
+<plain-text-body>{scrollbar}</plain-text-body></div>
</div>
<div class="clearer"></div>
Modified: websites/production/tapestry/content/release-notes-51.html
==============================================================================
--- websites/production/tapestry/content/release-notes-51.html (original)
+++ websites/production/tapestry/content/release-notes-51.html Tue Sep 26
19:20:27 2017
@@ -27,16 +27,6 @@
</title>
<link type="text/css" rel="stylesheet" href="/resources/space.css" />
- <link href='/resources/highlighter/styles/shCoreCXF.css'
rel='stylesheet' type='text/css' />
- <link href='/resources/highlighter/styles/shThemeCXF.css' rel='stylesheet'
type='text/css' />
- <script src='/resources/highlighter/scripts/shCore.js'
type='text/javascript'></script>
- <script src='/resources/highlighter/scripts/shBrushJava.js'
type='text/javascript'></script>
- <script src='/resources/highlighter/scripts/shBrushXml.js'
type='text/javascript'></script>
- <script src='/resources/highlighter/scripts/shBrushPlain.js'
type='text/javascript'></script>
- <script>
- SyntaxHighlighter.defaults['toolbar'] = false;
- SyntaxHighlighter.all();
- </script>
<link href="/styles/style.css" rel="stylesheet" type="text/css"/>
@@ -77,19 +67,12 @@
</div>
<div id="content">
- <div id="ConfluenceContent">
+ <div
id="ConfluenceContent"><plain-text-body>{scrollbar}</plain-text-body>
<p>This is the consolidated list of changes between Tapestry versions 5.0 and
5.1. Before upgrading, be sure to review the <a href="how-to-upgrade.html">How
to Upgrade</a> instructions.</p>
<p><strong>Contents</strong></p>
-<style type="text/css">/*<![CDATA[*/
-div.rbtoc1499639565943 {padding: 0px;}
-div.rbtoc1499639565943 ul {list-style: disc;margin-left: 0px;}
-div.rbtoc1499639565943 li {margin-left: 0px;padding-left: 0px;}
-
-/*]]>*/</style><div class="toc-macro rbtoc1499639565943">
-<ul class="toc-indentation"><li><a
href="#ReleaseNotes5.1-TapestryVersion5.1.0.5">Tapestry Version
5.1.0.5</a></li><li><a href="#ReleaseNotes5.1-TapestryVersion5.1.0.4">Tapestry
Version 5.1.0.4</a></li><li><a
href="#ReleaseNotes5.1-TapestryVersion5.1.0.3">Tapestry Version
5.1.0.3</a></li><li><a href="#ReleaseNotes5.1-TapestryVersion5.1.0.2">Tapestry
Version 5.1.0.2</a></li><li><a
href="#ReleaseNotes5.1-TapestryVersion5.1.0.1">Tapestry Version
5.1.0.1</a></li><li><a href="#ReleaseNotes5.1-TapestryVersion5.1.0.0">Tapestry
Version 5.1.0.0</a></li></ul>
-</div>
+<parameter ac:name="maxLevel">2</parameter><parameter
ac:name="minLevel">2</parameter>
<h2 id="ReleaseNotes5.1-TapestryVersion5.1.0.5">Tapestry Version 5.1.0.5</h2>
@@ -217,7 +200,8 @@ div.rbtoc1499639565943 li {margin-left:
<ul><li><a class="external-link"
href="https://issues.apache.org/jira/browse/TAP5-372">TAP5-372</a> –
Merge changes from 5.0.16 --> 5.0.17 into trunk (5.1)</li><li><a
class="external-link"
href="https://issues.apache.org/jira/browse/TAP5-379">TAP5-379</a> – Add
the Ars Machina Project to the list of Tapestry 5-related packages</li><li><a
class="external-link"
href="https://issues.apache.org/jira/browse/TAP5-381">TAP5-381</a> –
Documentation talks about a "tapestry.charset" when there's no such
configuration documented</li><li><a class="external-link"
href="https://issues.apache.org/jira/browse/TAP5-480">TAP5-480</a> –
Upgrade Surefire Plugin and TestNG dependencies to latest version (2.4.3 and
5.8, respectively)</li><li><a class="external-link"
href="https://issues.apache.org/jira/browse/TAP5-493">TAP5-493</a> –
Translate StructureStrings#original-child-component</li><li><a
class="external-link" href="https://issues.apache.org/jira/browse/TAP5-
494">TAP5-494</a> – Downgrade maven-site-plugin from 2.0-beta-6 to
2.0-beta-5 because we prefer a site that actually works</li></ul>
-</div>
+
+<plain-text-body>{scrollbar}</plain-text-body></div>
</div>
<div class="clearer"></div>