onActivate and onPassivate

Rule of Thumb

A good "rule of thumb" is to use onActivate(...) and onPassivate() for no more than receiving and returning the activation context.
In this page, for example, onActivate(...) receives a person id and onPassivate() returns a person id.

It can be tempting to put setup code into onActivate(...) but avoid this because onActivate(...) is called very often. There are better places:

We could put code in onActivate(...) to validate access to the page, but there are better ways - see the Protecting Pages example.

When is onActivate(...) called?

onActivate(...) is called in response to a page render request, a component event request, or when another page calls a method on the page - which is typically to set it up before returning it. onActivate(...) is not called when a PageLink to the page is rendered.

A page render request can provide an activation context for the new page. A component event request provides the activation context of the page it came from. When another page calls a method on the page it is usually to set up the fields used in the activation context.

When is onPassivate() called?

The purpose of onPassivate() is to return the activation context of the page instance. Tapestry calls onPassivate() in these situations:

  1. when rendering a PageLink that does not specify a context.
  2. when generating a page render request to return as part of Post/Redirect/Get (see Forms example).
Neither situation applies to this page, so onPassivate() is unnecessary in this page. However, by providing it we are preparing the page for those two situations. (Maybe this is violating the YAGNI principle, but in practice it can prevent confusion later).

Why are they package scoped?

This is just a personal style choice. Strictly speaking, onActivate(...) and onPassivate(..) should be private scoped because only Tapestry should call them, but then the compiler generates warnings that the methods are "unused". Rather than clutter the code with @SuppressWarnings("unused") annotations on the methods, in JumpStart we have taken the easy way out and used package scope for these methods. This is a common convention in Tapestry for event handler methods too.

For completeness, here is the Person specified by the activation context given to this page:
Id
3
Version
23
First Name
acme
Last Name
km
Region
West Coast
Start Date
Dec 14, 2023
References: Page Navigation, Component Events, @Cached.

Home

The source for IPersonFinderServiceLocal and @EJB is shown in the Session Beans and @EJB examples.


<!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_4.xsd">
<body class="container">
    <h3>onActivate and onPassivate</h3>
    
    <h4>Rule of Thumb</h4>

    A good "rule of thumb" is to use onActivate(...) and onPassivate() for no more than receiving and returning the 
    activation context.<br/>
    In this page, for example, onActivate(...) receives a person id and onPassivate() returns a person id.<br/><br/>

    <p>It can be tempting to put setup code into onActivate(...) but avoid this because onActivate(...) is called very often. 
    There are better places:</p>
    <ul>
    <li>
        <code>setupRender()</code> is ideal for setting up objects and fields for display-only because Tapestry 
        calls it only during a <a href="http://tapestry.apache.org/page-navigation.html#PageNavigation-PageRenderRequests">
        page render request</a>.
    </li>
    <li>
        <code>onPrepare()</code> is ideal for setting up objects and fields that can be edited in a Form because it 
        is triggered during form rendering and form submission but it isn't triggered by other 
        <a href="http://tapestry.apache.org/page-navigation.html#PageNavigation-ComponentEventRequests&amp;Responses">component event requests</a>. 
        If there's more than one form then use more than one onPrepare, eg. onPrepareFromFormX(), onPrepareFromFormY().
    </li>
    <li>
        <strong>getters</strong> <em>can</em> be used for setting up objects and fields, but exercise caution 
        because they can be called more often than you expect. In this situation consider lazy-loading, eg. with 
        the method-level annotation <code>@Cached</code>. See 
        <a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/annotations/Cached.html">@Cached</a>.  
    </li>
    </ul>
    
    We <em>could</em> put code in onActivate(...) to validate access to the page, but there are better ways - see 
    the Protecting Pages example.<br/>
    
    <h4>When is onActivate(...) called?</h4>
    
    onActivate(...) is called in response to a page render request, a component event request, or when another page 
    calls a method on the page - which is typically to set it up before returning it. 
    onActivate(...)  is <em>not</em> called when a PageLink to the page is rendered.<br/><br/>
    
    A <a href="http://tapestry.apache.org/page-navigation.html#PageNavigation-PageRenderRequests">page render request</a> 
    can provide an activation context for the new page. 
    A <a href="http://tapestry.apache.org/page-navigation.html#PageNavigation-ComponentEventRequests&amp;Responses">component 
    event request</a> provides the activation context of the page it came from. 
    When another page calls a method on the page it is usually to set up the fields used in the activation context.<br/>

    <h4>When is onPassivate() called?</h4>
    
    <p>The purpose of onPassivate() is to return the activation context of the page instance. Tapestry calls onPassivate() in 
    these situations:</p>
    <ol>
        <li>when rendering a PageLink that does not specify a context.</li>
        <li>when generating a page render request to return as part of Post/Redirect/Get (see Forms example).</li>
    </ol>
    Neither situation applies to this page, so onPassivate() is unnecessary in this page.  However, by providing it we are  
    preparing the page for those two situations. (Maybe this is violating the 
    <a href="http://en.wikipedia.org/wiki/YAGNI">YAGNI</a> principle, but in practice it can prevent confusion later).<br/>

    <h4>Why are they package scoped?</h4>
    
    This is just a personal style choice. Strictly speaking, onActivate(...) and onPassivate(..) should be private scoped 
    because only Tapestry should call them, but then the compiler generates warnings that the methods are "unused".  Rather 
    than clutter the code with @SuppressWarnings("unused") annotations on the methods, in JumpStart we have taken the easy 
    way out and used package scope for these methods. This is a common convention in Tapestry for event handler methods too.<br/><br/>
    
    For completeness, here is the Person specified by the activation context given to this page:
    
    <div class="eg">
        <t:beandisplay object="person"/>
    </div>

    References: 
    <a href="http://tapestry.apache.org/page-navigation.html">Page Navigation</a>, 
    <a href="http://tapestry.apache.org/component-events.html">Component Events</a>, 
    <a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/annotations/Cached.html">@Cached</a>.<br/><br/>

    <t:pagelink page="Index">Home</t:pagelink><br/><br/>
    
    The source for IPersonFinderServiceLocal and @EJB is shown in the Session Beans and @EJB examples.<br/><br/>

    <t:tabgroup>
        <t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/navigation/OnActivateAndOnPassivate.tml"/>
        <t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/navigation/OnActivateAndOnPassivate.java"/>
        <t:sourcecodetab src="/web/src/main/resources/META-INF/assets/css/examples/plain.css"/>
    </t:tabgroup>
</body>
</html>


package jumpstart.web.pages.examples.navigation;

import javax.ejb.EJB;

import jumpstart.business.domain.person.Person;
import jumpstart.business.domain.person.iface.IPersonFinderServiceLocal;

import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.Property;

@Import(stylesheet = "css/examples/plain.css")
public class OnActivateAndOnPassivate {

    // The activation context

    private Long personId;

    // Screen fields

    @Property
    private Person person;

    // Generally useful bits and pieces

    @EJB
    private IPersonFinderServiceLocal personFinderService;

    // The code

    // onActivate() is called by Tapestry to pass in the activation context from the URL.

    void onActivate(Long personId) {
        this.personId = personId;
    }

    // onPassivate() is called by Tapestry to get the activation context to put in the URL.

    Long onPassivate() {
        return personId;
    }

    // setupRender() is called by tapestry at the start of rendering - it's good for things that are display only.

    void setupRender() throws Exception {
        // Get person - ask business service to find it (from the database)
        person = personFinderService.findPerson(personId);
        
        if (person == null) {
            throw new Exception("Database data has not been set up!");
        }
    }
}


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