What is Called and When

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: And just briefly, a couple of important things a page template can do: OK, back to the demonstration. This page provides, and logs, examples of all the methods we've mentioned. 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 render request.
Eg. When a PageLink to this page is clicked,
or in the redirect that follows an event request.
  • The 3 onPassivate() calls are due to the EventLink, ActionLink, and Form on this page: those 3 components bubble up passivate before they render themselves. They do it to get the current activation context, which they put in the URL that they render.
  • The onPrepareForRender() and onPrepare() calls are due to the Form on this page: the Form component bubbles up prepareForRender and prepare before it renders itself.
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
In response to a Form event request.
Eg. Name:
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

message: This message is generated by getMessage().
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.

References: Page Life Cycle, Component Rendering, Component Events, Component Parameters, EventLink, ActionLink, Form, Submit, Logging.

Home

WhatIsCalledAndWhen.tml


<!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 &nbsp; 
     We use a "strict" DTD to make IE follow the alignment rules. -->
     
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
<head>
    <link rel="stylesheet" type="text/css" href="${context:css/examples/examples.css}"/>
    <style type="text/css">
        .eg { border: none; margin: 0; color: #333; }
        tr  { background-color: rgb(204, 190, 153); border: black 2px outset; padding: 2px; font-family: 'Trebuchet MS'; }
        th  { width:300px; text-align:left; font-weight: normal; padding: 0.4em; }
        td  { width:300px; text-align:left; font-weight: normal; padding: 0.4em; vertical-align: top;}
        .what   { font-weight: bold; }
    </style>
</head>
<body style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;">
    <h1>What is Called and When</h1>
    
    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.<br/><br/>
    
    But first, a review of what a <strong>page class</strong> can do and how. It can:
    <ul>
        <li><span class="what">Control rendering</span> (eg. for a render request) with 
            <a class="link" href="http://tapestry.apache.org/component-rendering.html#ComponentRendering-RenderingPhases">
            render phase methods</a>, ie. <em>setupRender()</em>, <em>cleanupRender()</em>, 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. <em>onGoHome(...)</em>, <em>onAction(...)</em>, <em>onPrepareForSubmit()</em>.</li>
        <li><span class="what">Expose data</span> to its template, with 
            properties, eg. <em>getFirstName()</em>, <em>setFirstName(...)</em>, or <em>@Property</em>.</li>
        <li><span class="what">Receive its context</span> from a 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. <em>onActivate(...)</em>.</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. <em>onPassivate()</em>.</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. <em>pageLoaded()</em>, <em>pageAttached()</em>, <em>pageDetached</em>.</li>
    </ul>
    
    And just briefly, a couple of important things a <strong>page template</strong> can do:
    <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. <em>${exampleExpansion1}</em>, <em>${exampleExpansion2}</em>.</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. <em>${exampleParameters1}</em>.</li>
    </ul>
                
    OK, back to the demonstration. This page provides, and logs, examples of all the methods we've mentioned. For convenience, <br/>
    the following table shows what you would see in the logs if you configured log4j to record this page at debug level:<br/>
    
    <div class="eg">
        <table style="border:thin gray 2px;">
            <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>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. <a t:type="EventLink" t:event="goHome" href="#">Home</a>
                </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. <a t:type="ActionLink" t:id="toHome" href="#">Home</a>
                </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>
                    <form t:type="form" t:id="form">
                        In response to a <strong>Form</strong> event request.<br/>
                        Eg. Name: <input t:type="TextField" t:id="name"/><input t:type="Submit" value="Submit"/>
                    </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><br/>
        
        message: ${message}
    </div>
    
    <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>.<br/><br/>
    
    <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.<br/><br/>
    
    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.3/apidocs/org/apache/tapestry5/corelib/components/EventLink.html">EventLink</a>, 
    <a href="http://tapestry.apache.org/5.3/apidocs/org/apache/tapestry5/corelib/components/ActionLink.html">ActionLink</a>, 
    <a href="http://tapestry.apache.org/5.3/apidocs/org/apache/tapestry5/corelib/components/Form.html">Form</a>, 
    <a href="http://tapestry.apache.org/5.3/apidocs/org/apache/tapestry5/corelib/components/Submit.html">Submit</a>, 
    <a href="http://tapestry.apache.org/logging.html">Logging</a>.<br/><br/>
    
    <a t:type="pagelink" t:page="Index" href="#">Home</a><br/><br/>
    
    <t:sourcecodedisplay src="/web/src/main/java/jumpstart/web/pages/examples/navigation/WhatIsCalledAndWhen.tml"/>
    <t:sourcecodedisplay src="/web/src/main/java/jumpstart/web/pages/examples/navigation/WhatIsCalledAndWhen.java"/>
    <t:sourcecodedisplay src="/web/src/main/java/jumpstart/web/css/examples/examples.css"/>
</body>
</html>

WhatIsCalledAndWhen.java


package jumpstart.web.pages.examples.navigation;

import jumpstart.web.pages.Index;

import org.apache.tapestry5.ioc.annotations.Inject;
import org.slf4j.Logger;

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 onPassivate() {
        logger.debug("...onPassivate()");
    }

    void onActivate() {
        logger.debug("...onActivate()");
    }

    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 t:value=\"firstName\"/>";
    }
}

examples.css


body            { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 13px; font-weight: normal; color: #333; line-height: 17px; }
h1              { font-size: 26px; line-height: 20px; } /* For IE 7 */
form            { margin: 0; }                  

.eg             { margin: 20px 0; padding: 20px; color: #888; 
                    border: 1px solid #ddd; border-radius: 4px; -webkit-border-radius: 4px; -mox-border-radius: 4px; }

a               { text-decoration: none; color: #3D69B6; }
a:hover         { text-decoration: underline; }

/* For BeanDisplay */
.eg dl          { margin: 0; color: #333; }
.eg dl.t-beandisplay dd.id  { display: inline; margin-left: 0px; }  /* IE 7 hack */