This page demonstrates what methods are called, and when, in various situations.
It does not cover AJAX requests - they are demonstrated in AJAX: What is Called and When.
But first, a review of what a page class can do and how. It can:
setupRender()
, cleanupRender()
, etc.onGoHome(...)
, onPrepareForSubmit()
.getFirstName()
, setFirstName(...)
, or @Property
.onActivate(...)
.onPassivate()
.pageLoaded()
, pageAttached()
, pageDetached
.And, just briefly, a couple of important things a page template can do - it can:
${firstName}
, ${context:images/icon.png}
.<t:TextField value="firstName"/>
.OK, back to the demonstration. This page provides examples of all the methods we've mentioned, and when they're called they write to the log.
For convenience, the following table shows what you would see in the logs if you configured log4j to record this page at DEBUG level:
When this page is first instantiated. | pageLoaded() |
---|---|
When Tapestry creates a URL to this page. Eg. when another page renders a PageLink to this page. |
These are called in this page: pageAttached() ...onPassivate() ...Tapestry creates a URL to this page pageDetached() |
In response to a page render request. Eg. When a PageLink to this page is clicked, or in the redirect that follows an event request.
|
pageAttached() ...onActivate() ...setupRender() ...beginRender() ...onPassivate() ...onPassivate() ...onPassivate() ...onPrepareForRender() ...onPrepare() ...getName() ...getMessage() ...afterRender() ...cleanupRender() pageDetached() Tapestry returns this page to browser |
In response to an EventLink event request. Eg. Home |
pageAttached() ...onActivate() ...onGoHome() ...Tapestry creates a URL to next page pageDetached() Tapestry redirects browser to URL of next page |
In response to ActionLink event request. Eg. Home |
pageAttached() ...onActivate() ...onAction() ...Tapestry creates a URL to next page pageDetached() Tapestry redirects browser to URL of next page |
pageAttached() ...onActivate() ...onPrepareForSubmit() ...onPrepare() ...onValidateFromName() ...setName() ...onSelected() ...onValidateFromForm() ...onSuccess() ...onSubmit() ...Tapestry creates a URL to next page pageDetached() Tapestry redirects browser to URL of next page |
PageAttached and PageDetached have been deprecated. However, they might be "de-deprecated" in future as discussed here.
About Thread Safety (based on an Lance's answer)
Each incoming request runs in its own thread, and Tapestry pages are singletons that are shared between threads...
So how does Tapestry avoid issues of synchronisation and conflicts of field values, ie. how does it achieve thread-safety?
The answer is byte-code manipulation: Tapestry transforms your pages and components to store their request-specific state in ThreadLocals.
In production mode, the ThreadLocal values are used. The page and component member variables are not used.
In development mode, the ThreadLocal values are used AND Tapestry mirrors them to the page and component member variables to make debugging easier.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- We need a doctype to allow us to use special characters like
We use a "strict" DTD to make IE follow the alignment rules. -->
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd">
<body class="container">
<h3>What is Called and When</h3>
<p>This page demonstrates what methods are called, and when, in various situations.<br/>
It does not cover AJAX requests - they are demonstrated in AJAX: What is Called and When.</p>
<p>But first, a review of what a <strong>page class</strong> can do and how. It can:</p>
<ul>
<li><span class="what">Control rendering</span> (eg. for a page render request) with
<a class="link" href="http://tapestry.apache.org/component-rendering.html#ComponentRendering-RenderingPhases">
render phase methods</a>, ie. <code>setupRender()</code>, <code>cleanupRender()</code>, etc.</li>
<li><span class="what">Handle events</span> (ie. events bubbled up from components) with
<a class="link" href="http://tapestry.apache.org/component-events.html#ComponentEvents-EventHandlerMethods">
event handler methods</a>, eg. <code>onGoHome(...)</code>, <code>onPrepareForSubmit()</code>.</li>
<li><span class="what">Expose data</span> to its template, with
properties, eg. <code>getFirstName()</code>, <code>setFirstName(...)</code>, or <code>@Property</code>.</li>
<li><span class="what">Receive its context</span> from a page render request or component event request, with an
<a class="link" href="http://tapestry.apache.org/page-navigation.html#PageNavigation-Pageactivation">
activate event handler</a>, ie. <code>onActivate(...)</code>.</li>
<li><span class="what">Return its context</span>, which Tapestry uses to generate a request URL to it, with a
<a class="link" href="http://tapestry.apache.org/page-navigation.html#PageNavigation-PageRenderRequests">
passivate event handler</a>, ie. <code>onPassivate()</code>.</li>
<li><span class="what">React to its life cycle</span> with
<a class="link" href="http://tapestry.apache.org/page-life-cycle.html#PageLifeCycle-PageLifeCycleMethods">page
life cycle methods</a> (rarely needed), ie. <code>pageLoaded()</code>, <code>pageAttached()</code>, <code>pageDetached</code>.</li>
</ul>
<p>And, just briefly, a couple of important things a <strong>page template</strong> can do - it can:</p>
<ul>
<li><span class="what">Render the page's properties and resources</span>, with
<a class="link" href="http://tapestry.apache.org/component-templates.html#ComponentTemplates-Expansions">
expansions</a>, eg. <code>${exampleExpansion1}</code>, <code>${exampleExpansion2}</code>.</li>
<li><span class="what">Bind the page's properties and resources</span> to components via
<a class="link" href="http://tapestry.apache.org/component-parameters.html">
parameters</a>, eg. <code>${exampleParameters1}</code>.</li>
</ul>
<p>OK, back to the demonstration. This page provides examples of all the methods we've mentioned, and when they're called they write to the log.<br/>
For convenience, the following table shows what you would see in the logs if you configured log4j to record this page at DEBUG level:</p>
<div class="eg row">
<div class="col-sm-9 col-sm-offset-1">
<table class="table table-bordered">
<tr>
<th>
When this page is <strong>first instantiated</strong>.
</th>
<td>
pageLoaded()
</td>
</tr>
<tr>
<th>
When Tapestry creates <strong>a URL to this page</strong>.<br/>
Eg. when another page renders a PageLink to this page.
</th>
<td>
<em>These are called in this page:</em><br/>
pageAttached()<br/>
...onPassivate()<br/>
<em>...Tapestry creates a URL to this page</em><br/>
pageDetached()<br/>
</td>
</tr>
<tr>
<th>
In response to a <strong>page render request</strong>.<br/>
Eg. When a PageLink to this page is clicked, <br/>
or in the redirect that follows an event request.<br/>
<ul>
<li>The 3 <em>onPassivate()</em> calls are due to the EventLink, ActionLink, and Form on this page: those 3 components
bubble up <em>passivate</em> before they render themselves. They do it to get the current activation context, which
they put in the URL that they render.</li>
<li>The <em>onPrepareForRender()</em> and <em>onPrepare()</em> calls are due to the Form on this page: the Form component
bubbles up <em>prepareForRender</em> and <em>prepare</em> before it renders itself.</li>
</ul>
</th>
<td>
pageAttached()<br/>
...onActivate()<br/>
...setupRender()<br/>
...beginRender()<br/>
...onPassivate()<br/>
...onPassivate()<br/>
...onPassivate()<br/>
...onPrepareForRender()<br/>
...onPrepare()<br/>
...getName()<br/>
...getMessage()<br/>
...afterRender()<br/>
...cleanupRender()<br/>
pageDetached()<br/>
<em>Tapestry returns this page to browser</em>
</td>
</tr>
<tr>
<th>
In response to an <strong>EventLink</strong> event request.<br/>
Eg. <t:eventlink event="goHome">Home</t:eventlink>
</th>
<td>
pageAttached()<br/>
...onActivate()<br/>
...onGoHome()<br/>
<em>...Tapestry creates a URL to next page</em><br/>
pageDetached()<br/>
<em>Tapestry redirects browser to URL of next page</em>
</td>
</tr>
<tr>
<th>
In response to <strong>ActionLink</strong> event request.<br/>
Eg. <t:actionlink t:id="toHome">Home</t:actionlink>
</th>
<td>
pageAttached()<br/>
...onActivate()<br/>
...onAction()<br/>
<em>...Tapestry creates a URL to next page</em><br/>
pageDetached()<br/>
<em>Tapestry redirects browser to URL of next page</em>
</td>
</tr>
<tr>
<th>
<t:form t:id="form">
In response to a <strong>Form</strong> event request.
<div class="form-group form-inline">
Eg. Name:
<t:textfield t:id="name"/>
<t:submit/>
</div>
</t:form>
</th>
<td>
pageAttached()<br/>
...onActivate()<br/>
...onPrepareForSubmit()<br/>
...onPrepare()<br/>
...onValidateFromName()<br/>
...setName()<br/>
...onSelected()<br/>
...onValidateFromForm()<br/>
...onSuccess()<br/>
...onSubmit()<br/>
<em>...Tapestry creates a URL to next page</em><br/>
pageDetached()<br/>
<em>Tapestry redirects browser to URL of next page</em>
</td>
</tr>
</table>
message: ${message}
</div>
</div>
<p><a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/PageAttached.html">PageAttached</a> and
<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/PageDetached.html">PageDetached</a>
have been deprecated. However, they might be "de-deprecated" in future
<a href="http://tapestry.1045711.n5.nabble.com/Which-phase-of-page-lifecycle-will-not-occur-when-we-access-page-2nd-time-td5718142.html#none">
as discussed here</a>.</p>
<p><strong>About Thread Safety</strong> (based on an <a href="http://tapestry.1045711.n5.nabble.com/Which-phase-of-page-lifecycle-will-not-occur-when-we-access-page-2nd-time-td5718142.html#none">Lance's answer</a>)<br/>
Each incoming request runs in its own thread, and Tapestry pages are singletons that are shared between threads...<br/>
So how does Tapestry avoid issues of synchronisation and conflicts of field values, ie. how does it achieve thread-safety?<br/>
The answer is byte-code manipulation: Tapestry transforms your pages and components to store their request-specific state in ThreadLocals.<br/>
<strong>In production mode</strong>, the ThreadLocal values are used. The page and component member variables are not used.<br/>
<strong>In development mode</strong>, the ThreadLocal values are used AND Tapestry mirrors them to the page and component member variables to make debugging easier.</p>
References:
<a href="http://tapestry.apache.org/page-life-cycle.html">Page Life Cycle</a>,
<a href="http://tapestry.apache.org/component-rendering.html">Component Rendering</a>,
<a href="http://tapestry.apache.org/component-events.html">Component Events</a>,
<a href="http://tapestry.apache.org/component-parameters.html">Component Parameters</a>,
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/corelib/components/EventLink.html">EventLink</a>,
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/corelib/components/ActionLink.html">ActionLink</a>,
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/corelib/components/Form.html">Form</a>,
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/corelib/components/Submit.html">Submit</a>,
<a href="http://tapestry.apache.org/logging.html">Logging</a>.<br/><br/>
<t:pagelink page="Index">Home</t:pagelink><br/><br/>
<t:tabgroup>
<t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/navigation/WhatIsCalledAndWhen.tml"/>
<t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/navigation/WhatIsCalledAndWhen.java"/>
<t:sourcecodetab src="/web/src/main/resources/META-INF/assets/css/examples/whatiscalledandwhen.css"/>
</t:tabgroup>
</body>
</html>
package jumpstart.web.pages.examples.navigation;
import jumpstart.web.pages.Index;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.slf4j.Logger;
@Import(stylesheet="css/examples/whatiscalledandwhen.css")
public class WhatIsCalledAndWhen {
// Screen fields
private String name;
// Generally useful bits and pieces
@Inject
private Logger logger;
// The code
void pageLoaded() {
logger.debug(" ");
logger.debug("pageLoaded()");
}
void pageAttached() {
logger.debug(" ");
logger.debug("pageAttached()");
}
void pageDetached() {
logger.debug("pageDetached()");
}
void onActivate() {
logger.debug("...onActivate()");
}
void onPassivate() {
logger.debug("...onPassivate()");
}
void setupRender() {
logger.debug("...setupRender()");
}
void beginRender() {
logger.debug("...beginRender()");
}
void afterRender() {
logger.debug("...afterRender()");
}
void cleanupRender() {
logger.debug("...cleanupRender()");
}
Object onGoHome() {
logger.debug("...onGoHome()");
return Index.class;
}
Object onAction() {
logger.debug("...onAction()");
return Index.class;
}
void onPrepareForRender() {
logger.debug("...onPrepareForRender()");
}
void onPrepare() {
logger.debug("...onPrepare()");
}
void onPrepareForSubmit() {
logger.debug("...onPrepareForSubmit()");
}
void onValidateFromName() {
logger.debug("...onValidateFromName()");
}
void onSelected() {
logger.debug("...onSelected()");
}
void onValidateFromForm() {
logger.debug("...onValidateFromForm()");
}
void onSuccess() {
logger.debug("...onSuccess()");
}
void onFailure() {
logger.debug("...onFailure()");
}
Object onSubmit() {
logger.debug("...onSubmit()");
return Index.class;
}
public String getName() {
logger.debug("...getName()");
return name;
}
public void setName(String name) {
logger.debug("...setName()");
this.name = name;
}
public String getMessage() {
logger.debug("...getMessage()");
return "This message is generated by getMessage().";
}
public String getExampleExpansion1() {
return "${firstName}";
}
public String getExampleExpansion2() {
return "${context:images/icon.png}";
}
public String getExampleParameters1() {
return "<t:TextField value=\"firstName\"/>";
}
}
.eg {
margin: 20px 0;
}
table {
background-color: rgb(244,248,250);
}
th {
width: 300px;
font-weight: normal;
}
td {
width: 300px;
font-weight: normal;
}
table>tbody>tr:last-child {
border-bottom: 1px solid #dddddd;
}
#name {
width: 40%;
}