tapestry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Tapestry > Component Classes
Date Tue, 10 Jan 2012 07:47:00 GMT
<html>
<head>
    <base href="https://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/2042/9/12/_/styles/combined.css?spaceKey=TAPESTRY&amp;forWysiwyg=true"
type="text/css">
    </head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
    <h2><a href="https://cwiki.apache.org/confluence/display/TAPESTRY/Component+Classes">Component
Classes</a></h2>
    <h4>Page <b>edited</b> by             <a href="https://cwiki.apache.org/confluence/display/~kaosko">Kalle
Korhonen</a>
    </h4>
        <br/>
                         <h4>Changes (1)</h4>
                                 
    
<div id="page-diffs">
                    <table class="diff" cellpadding="0" cellspacing="0">
    
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" > <br>Unless an instance variable
is decorated with an annotation, it will be a _transient_ instance variable. This means that
its value resets to its default value at the end of reach request (when the [page is detached
from the request|Page Life Cycle]). <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">
<br>{note:title=About initialization} <br>Never initialize an instance field to
a mutable object at the point of declaration.  If this is done, the instance created from
that initializer becomes the default value for that field and is reused inside the component
on every request.  This could cause state to inadvertently be shared between different sessions
in an application. <br>{note} <br> <br> <br></td></tr>
            <tr><td class="diff-unchanged" >{deprecated:since=5.2}For Tapestry
5.1 and earlier, if you have a variable that can keep its value between requests and you would
like to defeat that reset logic, then you can add a @[Retain|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Retain.html]
annotation to the field. You should take care that no client-specific data is stored into
such a field, since on a later request the same page _instance_ may be used for a different
user. Likewise, on a later request for the _same_ user, a _different_ page instance may be
used. <br>{deprecated} <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
    
            </table>
    </div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <h1><a name="ComponentClasses-ComponentClasses"></a>Component Classes</h1>

<div class='navmenu' style='float:right; background:#eee; margin:3px; padding:3px'><table
class="tableview" width="100%">
            <tr><th style="padding: 3px 3px 3px 0px">Related Articles</th></tr>
                        <tr>
            <td>
                                 <span class="icon icon-page" title=Page>Page:</span>
                         <a href="/confluence/display/TAPESTRY/Component+Libraries">Component
Libraries</a>
        
                                            </td>
        </tr>
                <tr>
            <td>
                                 <span class="icon icon-page" title=Page>Page:</span>
                         <a href="/confluence/display/TAPESTRY/Component+Cheat+Sheet">Component
Cheat Sheet</a>
        
                                            </td>
        </tr>
                <tr>
            <td>
                                 <span class="icon icon-page" title=Page>Page:</span>
                         <a href="/confluence/display/TAPESTRY/Page+And+Component+Classes+FAQ">Page
And Component Classes FAQ</a>
        
                                            </td>
        </tr>
                <tr>
            <td>
                                 <span class="icon icon-page" title=Page>Page:</span>
                         <a href="/confluence/display/TAPESTRY/Component+Parameters">Component
Parameters</a>
        
                                            </td>
        </tr>
                <tr>
            <td>
                                 <span class="icon icon-page" title=Page>Page:</span>
                         <a href="/confluence/display/TAPESTRY/Component+Reference">Component
Reference</a>
        
                                            </td>
        </tr>
                <tr>
            <td>
                                 <span class="icon icon-page" title=Page>Page:</span>
                         <a href="/confluence/display/TAPESTRY/Component+Templates">Component
Templates</a>
        
                                            </td>
        </tr>
            </table>
</div> 

<p>A <b>Component class</b> is the class associated with a page, component
or mixin in your Tapestry web application. Classes for pages, components and component mixins
are all created in an identical way. They are pure POJOs (Plain Old Java Objects), with annotations.
 They are not <em>abstract</em>, nor do they extend from framework base classes.</p>

<p><em>For Tapestry 4 Users: Component classes in Tapestry 5 are much easier than
in Tapestry 4. There are no base classes to extend from, the classes are concrete (not abstract),
and there's no XML file. There is still a bit of configuration in the form of Java annotations,
but those now go directly onto fields of your class, rather than on abstract getters and setters.</em></p>

<p>In most cases, each component class will have a corresponding <a href="/confluence/display/TAPESTRY/Component+Templates"
title="Component Templates">component template</a>.  However, it is also possible
for a component class to emit all of its markup itself, without using a template.</p>

<h2><a name="ComponentClasses-ComponentClassBasics"></a>Component Class
Basics</h2>

<p>Creating page and component classes in Tapestry 5 is a breeze. There are only a few
constraints:</p>

<ul>
	<li>The classes must be public.</li>
	<li>The classes must be in the correct package (see below).</li>
	<li>The class must have a standard public, no-arguments constructor.</li>
</ul>


<p>Here's a very basic component:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">package</span> org.example.myapp.components;

<span class="code-keyword">import</span> org.apache.tapestry5.MarkupWriter;
<span class="code-keyword">import</span> org.apache.tapestry5.annotations.BeginRender;

<span class="code-keyword">public</span> class HelloWorld
{
    @BeginRender
    void renderMessage(MarkupWriter writer)
    {
        writer.write(<span class="code-quote">"Bonjour from HelloWorld component."</span>);
    }
}
</pre>
</div></div>

<p>In this example, the component's only job is to write out a fixed message. The @<a
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/BeginRender.html"
class="external-link" rel="nofollow">BeginRender</a> annotation is a type of <em><a
href="/confluence/display/TAPESTRY/Component+Rendering" title="Component Rendering">component
life cycle annotation</a></em>, a method annotation that instructs Tapestry when
and under what circumstances to invoke methods of your class.</p>

<p>These methods are not necessarily public; they can have any visibility you like (unlike
in Tapestry 4).</p>

<h2><a name="ComponentClasses-ComponentPackages"></a>Component Packages</h2>

<p>Component classes must exist within an appropriate package (this is necessary for
runtime code transformation and class reloading to operate).</p>

<p>These packages exist under the application's root package, as follows:</p>

<ul>
	<li>For pages, place classes in <em>root</em>.<b>pages</b>.
Page names are mapped to classes within this package.</li>
	<li>For components, place classes in <em>root</em>.<b>components</b>.
Component types are mapped to classes within this package.</li>
	<li>For mixins, place classes in <em>root</em>.<b>mixins</b>.
Mixin types are mapped to classes within this package.</li>
</ul>


<p>In addition, it is common for an application to have base classes, often <em>abstract</em>
base classes, that should not be directly referenced. These should <em>not</em>
go in the <b>pages</b>, <b>components</b> or <b>mixins</b>
packages, because they then look like valid pages, components or mixins. Instead, use the
<em>root</em>.<b>base</b> package to store such base classes.</p>

<h2><a name="ComponentClasses-SubFolders%2FSubPackages"></a>Sub-Folders
/ Sub-Packages</h2>

<p>Classes do not have to go directly inside the package (pages, components, mixins,
etc.). It is valid to create a sub-package to store some of the classes. The sub-package name
becomes part of the page name or component type. Thus you might define a page component <tt>com.example.myapp.pages.admin.CreateUser</tt>
and the logical page name (which often shows up inside URLs) will be <b>admin/CreateUser</b>.</p>

<p>Tapestry performs some simple optimizations of the logical page name (or component
type, or mixin type). It checks to see if the package name is either a prefix or a suffix
of the unqualified class name (case insensitively, of course) and removes the prefix or suffix
if so. The net result is that a class name such as <tt>com.example.myapp.pages.user.EditUser</tt>
will have a page name of <tt>user/Edit</tt> (<em>not</em> <tt>user/EditUser</tt>).
The goal here is to provide shorter, more natural URLs.</p>

<h2><a name="ComponentClasses-IndexPages"></a>Index Pages</h2>

<p>One special simplification are Index pages: if the logical page name is Index after
removing the package name from the unqualified class name, it will map to the root of that
folder. A class such as <tt>com.example.myapp.pages.user.IndexUser</tt> or <tt>com.example.myapp.pages.user.UserIndex</tt>
will have a page name of <tt>user/</tt>.</p>

<p>In previous versions of Tapestry there was also the concept of a start page configured
with the <tt><a href="/confluence/display/TAPESTRY/Configuration#Configuration-tapestry.startpagename">tapestry.start-page-name</a></tt>
configuration symbol (defaults to "start"). If a page with a name as configured with that
symbol exists at the root level, this page is used as the root URL. This has precedence over
an existing Index page. If for example you have a page class <tt>com.example.myapp.pages.Start</tt>
it will map to <tt>/</tt>.</p>

<div class='panelMacro'><table class='warningMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/forbidden.gif" width="16"
height="16" align="absmiddle" alt="" border="0"></td><td>Use of start-pages
is discouraged and support for it will eventually be removed. Use an Index page instead.</td></tr></table></div>

<h2><a name="ComponentClasses-Pagesvs.Components"></a>Pages vs. Components</h2>

<p>The distinction between pages and component is very, very small. The only real difference
is the package name: <em>root</em>.<b>pages</b>.<em>PageName</em>
for pages, and <em>root</em>.<b>components</b>.<em>ComponentType</em>
for components. Conceptually, page components are simply the <em>root component</em>
of a page's component tree.</p>

<p><em>For Tapestry 4 users: there was a much greater distinction in Tapestry
4 between pages and components, which showed up as separate interfaces and a hierarchy of
abstract implementations to extend your classes from.</em></p>

<h2><a name="ComponentClasses-ClassTransformation"></a>Class Transformation</h2>

<p>Tapestry uses your class as a starting point. It <em>transforms</em>
your class at runtime. This is necessary for a number of reasons, including to address how
Tapestry shares pages between requests.</p>

<p>For the most part, these transformations are both sensible and invisible. In a few
limited cases, they are marginally <a href="http://www.joelonsoftware.com/printerFriendly/articles/LeakyAbstractions.html"
class="external-link" rel="nofollow">leaky</a> &#8211; for instance, the scope
restrictions on instance variables described below &#8211; but we feel that the programming
model in general will support very high levels of developer productivity.</p>

<p>Because transformation doesn't occur until <em>runtime</em>, the build
stage of your application is not affected by the fact that you are creating a Tapestry application.
Further, your classes are absolutely simple POJOs during unit testing.</p>

<h2><a name="ComponentClasses-LiveClassReloading"></a>Live Class Reloading</h2>

<p>Main Article: <a href="/confluence/display/TAPESTRY/Class+Reloading" title="Class
Reloading">Class Reloading</a></p>

<p>Component classes are monitored for changes by the framework. <a href="/confluence/display/TAPESTRY/Class+Reloading"
title="Class Reloading">Classes are reloaded when changed</a>. This allows you to
build your application with a speed approaching that of a scripting environment, without sacrificing
any of the power of the Java platform.</p>

<p>And it's fast&#33; You won't even notice that this magic class reloading has
occurred.</p>

<p>The net result: super productivity &#8212; change your class, see the change
instantly. This is designed to be a blend of the best of scripting environments (such as Python
or Ruby) with all the speed and power of Java backing it up.</p>

<p>However, class reloading <em>only</em> applies to component classes and,
starting in 5.2, Tapestry IOC-based service implementations (with some restrictions). Other
classes, such as service interfaces, entity/model classes, and other data objects, are loaded
by the normal class loader and not subject to live class reloading.</p>

<h2><a name="ComponentClasses-InstanceVariables"></a>Instance Variables</h2>

<p>Tapestry components may have instance variables (unlike Tapestry 4, where you had
to use <em>abstract properties</em>).</p>

<p><b>Instance variables must be private.</b> (In Tapestry 5.3.2 and later
they can protected or package private as well &#8211; or even public, if marked <em>final</em>,
or annotated with @Retain). These scope restrictions are necessary in order for Tapestry to
perform runtime class modifications to support instance variables. You may have instance variables
with other scopes in your class, but you may then see unexpected behavior in a production
application because of how Tapestry shares and reuses pages and components. Tapestry will
log an error for each component class that contains instance variables that violate these
scope restrictions.</p>

<p>Be aware that you will need to provide getter and setter methods to access your classes'
instance variables. Tapestry <em>does not</em> do this automatically unless you
provide the @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Property.html"
class="external-link" rel="nofollow">Property</a> annotation on the field.</p>

<h2><a name="ComponentClasses-TransientInstanceVariables"></a>Transient
Instance Variables</h2>

<p>Unless an instance variable is decorated with an annotation, it will be a <em>transient</em>
instance variable. This means that its value resets to its default value at the end of reach
request (when the <a href="/confluence/display/TAPESTRY/Page+Life+Cycle" title="Page Life
Cycle">page is detached from the request</a>).</p>

<div class='panelMacro'><table class='noteMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/warning.gif" width="16" height="16"
align="absmiddle" alt="" border="0"></td><td><b>About initialization</b><br
/>Never initialize an instance field to a mutable object at the point of declaration. 
If this is done, the instance created from that initializer becomes the default value for
that field and is reused inside the component on every request.  This could cause state to
inadvertently be shared between different sessions in an application.</td></tr></table></div>




<div class='panelMacro'><table class='warningMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/forbidden.gif" width="16"
height="16" align="absmiddle" alt="" border="0"></td><td><b>Deprecated
since 5.2</b><br /></td></tr></table></div>
<div style="border-right: 20px solid #ffcccc;border-left: 20px solid #ffcccc;"><p>For
Tapestry 5.1 and earlier, if you have a variable that can keep its value between requests
and you would like to defeat that reset logic, then you can add a @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Retain.html"
class="external-link" rel="nofollow">Retain</a> annotation to the field. You should
take care that no client-specific data is stored into such a field, since on a later request
the same page <em>instance</em> may be used for a different user. Likewise, on
a later request for the <em>same</em> user, a <em>different</em> page
instance may be used.</p>
</div>

<p>Use <a href="/confluence/display/TAPESTRY/Persistent+Page+Data" title="Persistent
Page Data">persistent fields</a> to hold information from one request to the next.</p>

<p>Further, final fields are (in fact) final, and will not be reset between requests.</p>

<h2><a name="ComponentClasses-Constructors"></a>Constructors</h2>

<p>Tapestry will instantiate your class using the default, no arguments constructor.
Other constructors will be ignored.</p>

<h2><a name="ComponentClasses-Injection"></a>Injection</h2>

<p><a href="/confluence/display/TAPESTRY/Injection" title="Injection">Injection</a>
of dependencies occurs at the field level, via additional annotations. At runtime, fields
that contain injections become read-only.</p>

<h2><a name="ComponentClasses-Parameters"></a>Parameters</h2>

<p><a href="/confluence/display/TAPESTRY/Component+Parameters" title="Component Parameters">Component
parameters</a> are also identified using private fields of your class, with the @<a
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Parameter.html"
class="external-link" rel="nofollow">Parameter</a> annotation.</p>

<h2><a name="ComponentClasses-PersistentFields"></a>Persistent Fields</h2>

<p>Main Article: <a href="/confluence/display/TAPESTRY/Persistent+Page+Data" title="Persistent
Page Data">Persistent Page Data</a></p>

<p>Most fields in component classes are automatically cleared at the end of each request.
However, fields may be annotated so that they retain their value across requests, using the
@<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Persist.html"
class="external-link" rel="nofollow">Persist</a> annotation.</p>

<h2><a name="ComponentClasses-EmbeddedComponents"></a><a name="ComponentClasses-EmbeddedComponents"></a>Embedded
Components</h2>

<p>Components often contain other components. Components inside another component's
template are called <em>embedded components</em>. The containing component's <a
href="/confluence/display/TAPESTRY/Component+Templates" title="Component Templates">template</a>
will contain special elements, in the Tapestry namespace, identifying where the the embedded
components go.</p>

<p>You can define the type of component inside template, or you can create an instance
variable for the component and use the @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Component.html"
class="external-link" rel="nofollow">Component</a> annotation to define the component
type and parameters.</p>

<p>Example:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">package</span> org.example.app.pages;

<span class="code-keyword">import</span> org.apache.tapestry5.annotations.Component;
<span class="code-keyword">import</span> org.apache.tapestry5.annotations.Property;
<span class="code-keyword">import</span> org.example.app.components.Count;

<span class="code-keyword">public</span> class Countdown
{
    @Component(parameters =
    { <span class="code-quote">"start=5"</span>, <span class="code-quote">"end=1"</span>,
<span class="code-quote">"value=countValue"</span> })
    <span class="code-keyword">private</span> Count count;

    @Property
    <span class="code-keyword">private</span> <span class="code-object">int</span>
countValue;
}
</pre>
</div></div>

<p>The above defines a component whose embedded id is "count" (this id is derived from
the name of the field and an element with that id must be present in the corresponding template,
otherwise an error is displayed (see below)). The type of the component is org.example.app.components.Count.
The start and end parameters of the Count component are bound to literal values, and the value
parameter of the Count component is bound to the countValue property of the Countdown component.</p>

<p>Technically, the start and end parameters should be bound to properties, just the
the value parameter. However, certain literal values, such as the numeric literals in the
example, are accepted by the <tt>prop:</tt> binding prefix even though they are
not actually properties (this is largely as a convenience to the application developer). We
could also use the <tt>literal:</tt> prefix, <tt>"start=literal:5"</tt>,
which accomplishes largely the same thing.</p>

<p>You may specify additional parameters inside the component template, but parameters
in the component class take precedence.</p>

<p><b>TODO: May want a more complex check; what if user uses prop: in the template
and there's a conflict?</b></p>

<p>You may override the default component id (as derived from the field name) using
the id() attribute of the Component annotation.</p>

<p>If you define a component in the component class, and there is no corresponding element
in the template, Tapestry will log an error. In the example above that would be the case if
the template for the Countdown page didn't contain an element with <tt>&lt;t:count
t:id="count"&gt;</tt>.</p>
    </div>
        <div id="commentsSection" class="wiki-content pageSection">
        <div style="float: right;">
            <a href="https://cwiki.apache.org/confluence/users/viewnotifications.action"
class="grey">Change Notification Preferences</a>
        </div>
        <a href="https://cwiki.apache.org/confluence/display/TAPESTRY/Component+Classes">View
Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=21792148&revisedVersion=18&originalVersion=17">View
Changes</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message