tapestry-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Tapestry > Exploring the Project
Date Tue, 07 Dec 2010 11:50:00 GMT
<html>
<head>
    <base href="https://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/1810/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/Exploring+the+Project">Exploring
the Project</a></h2>
    <h4>Page <b>edited</b> by             <a href="https://cwiki.apache.org/confluence/display/~bobharner">Bob
Harner</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" >* All non-static fields must be *private*
<br> <br></td></tr>
            <tr><td class="diff-changed-lines" >As we saw when running the application,
the page displays the current date and time. The {{currentTime}} property is where that value
comes from; shortly we&#39;ll see how that value is referenced in the template, so it
can <span class="diff-added-words"style="background-color: #dfd;">be</span> extracted
from the page and output. <br></td></tr>
            <tr><td class="diff-unchanged" > <br>Tapestry always matches
a page class to a template; neither is functional without the other.  In fact, components
within a page are treated the same way (except that components do not always have templates).
<br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
        </table>
</div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <style type='text/css'>/*<![CDATA[*/
table.ScrollbarTable  {border: none;padding: 3px;width: 100%;padding: 3px;margin: 0px;background-color:
#f0f0f0}
table.ScrollbarTable td.ScrollbarPrevIcon {text-align: center;width: 16px;border: none;}
table.ScrollbarTable td.ScrollbarPrevName {text-align: left;border: none;}
table.ScrollbarTable td.ScrollbarParent {text-align: center;border: none;}
table.ScrollbarTable td.ScrollbarNextName {text-align: right;border: none;}
table.ScrollbarTable td.ScrollbarNextIcon {text-align: center;width: 16px;border: none;}

/*]]>*/</style><div class="Scrollbar"><table class='ScrollbarTable'><tr><td
class='ScrollbarPrevIcon'><a href="/confluence/display/TAPESTRY/Loading+the+Project+Into+Eclipse"><img
border='0' align='middle' src='/confluence/images/icons/back_16.gif' width='16' height='16'></a></td><td
width='33%' class='ScrollbarPrevName'><a href="/confluence/display/TAPESTRY/Loading+the+Project+Into+Eclipse">Loading
the Project Into Eclipse</a>&nbsp;</td><td width='33%' class='ScrollbarParent'><sup><a
href="/confluence/display/TAPESTRY/Tapestry+Tutorial"><img border='0' align='middle'
src='/confluence/images/icons/up_16.gif' width='8' height='8'></a></sup><a
href="/confluence/display/TAPESTRY/Tapestry+Tutorial">Tapestry Tutorial</a></td><td
width='33%' class='ScrollbarNextName'>&nbsp;<a href="/confluence/display/TAPESTRY/Implementing+the+Hi-Lo+Guessing+Game">Implementing
the Hi-Lo Guessing Game</a></td><td class='ScrollbarNextIcon'><a href="/confluence/display/TAPESTRY/Implementing+the+Hi-Lo+Guessing+Game"><img
border='0' align='middle' src='/confluence/images/icons/forwd_16.gif' width='16' height='16'></a></td></tr></table></div>

<p>The layout of the project follows the sensible standards promoted by Maven:</p>

<ul>
	<li>Java source files under <tt>src/main/java</tt></li>
	<li>Web application files under <tt>src/main/webapp</tt> (including <tt>src/main/webapp/WEB-INF</tt>)</li>
	<li>Java test sources under <tt>src/test/java</tt></li>
	<li>Non-code resources (including Tapestry component templates) under <tt>src/main/resources</tt>
and <tt>src/test/resources</tt></li>
</ul>


<p>Let's look at what Maven has created from the  archetype, starting with the web.xml
configuration file:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>src/main/webapp/WEB-INF/web.xml</b></div><div
class="codeContent panelContent">
<pre class="code-java">
&lt;?xml version=<span class="code-quote">"1.0"</span> encoding=<span class="code-quote">"UTF-8"</span>?&gt;
&lt;!DOCTYPE web-app
        PUBLIC <span class="code-quote">"-<span class="code-comment">//Sun Microsystems,
Inc.//DTD Web Application 2.3//EN"</span>
</span>        <span class="code-quote">"http:<span class="code-comment">//java.sun.com/dtd/web-app_2_3.dtd"</span>&gt;
</span>&lt;web-app&gt;
    &lt;display-name&gt;tutorial1 Tapestry 5 Application&lt;/display-name&gt;
    &lt;context-param&gt;
        &lt;!-- The only significant configuration <span class="code-keyword">for</span>
Tapestry 5, <span class="code-keyword">this</span> informs Tapestry
of where to look <span class="code-keyword">for</span> pages, components and mixins.
--&gt;
        &lt;param-name&gt;tapestry.app-<span class="code-keyword">package</span>&lt;/param-name&gt;
        &lt;param-value&gt;com.example.tutorial&lt;/param-value&gt;
    &lt;/context-param&gt;
    &lt;filter&gt;
        &lt;filter-name&gt;app&lt;/filter-name&gt;
        &lt;filter-class&gt;org.apache.tapestry5.TapestryFilter&lt;/filter-class&gt;
    &lt;/filter&gt;
    &lt;filter-mapping&gt;
        &lt;filter-name&gt;app&lt;/filter-name&gt;
        &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
    &lt;/filter-mapping&gt;
&lt;/web-app&gt;
</pre>
</div></div>

<p>This is short and sweet: you can see that the package name you provided earlier shows
up as the <tt>tapestry.app-package</tt> context parameter; the TapestryFilter
instance will use this information to locate the Java classes for pages and components.</p>

<p>Tapestry 5 operates as a <em>servlet filter</em> rather than as a traditional
<em>servlet</em>. In this way, Tapestry has a chance to intercept all incoming
requests, to determine which ones apply to Tapestry pages (or other resources). The net effect
is that you don't have to maintain any additional configuration for Tapestry to operate, regardless
of how many pages or components you add to your application.</p>

<p>Tapestry pages minimally consist of an ordinary Java class plus a component template
file.</p>

<p>In the root of your web application, a page named "Index" will be used for any request
that specifies no additional path after the context name.  </p>

<h1><a name="ExploringtheProject-IndexJavaClass"></a>Index Java Class</h1>

<p>Tapestry has very specific rules for where page classes go. Tapestry adds a sub-package,
"pages", to the root application package ("com.example.tutorial"); the Java classes for pages
goes there. Thus the full Java class name is com.example.tutorial.pages.Index.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>src/main/java/com/example/tutorial/pages/Index.java</b></div><div
class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">package</span> org.apache.tapestry5.tutorial.pages;

<span class="code-keyword">import</span> java.util.Date;

/**
 * Start page of application tutorial1.
 */
<span class="code-keyword">public</span> class Index
{
  <span class="code-keyword">public</span> Date getCurrentTime()
  {
    <span class="code-keyword">return</span> <span class="code-keyword">new</span>
Date();
  }
}
</pre>
</div></div>

<p>That's pretty darn simple: No classes to extend, no interfaces to implement, just
a very pure POJO (Plain Old Java Object). You do have to meet the Tapestry framework halfway:</p>

<ul>
	<li>You need to put the Java class in the expected package, org.apache.tapestry5.tutorial.pages</li>
	<li>The class must be public</li>
	<li>You need to make sure there's a public, no-arguments constructor (here, the Java
compiler has silently provided one for us)</li>
	<li>All non-static fields must be <b>private</b></li>
</ul>


<p>As we saw when running the application, the page displays the current date and time.
The <tt>currentTime</tt> property is where that value comes from; shortly we'll
see how that value is referenced in the template, so it can be extracted from the page and
output.</p>

<p>Tapestry always matches a page class to a template; neither is functional without
the other.  In fact, components within a page are treated the same way (except that components
do not always have templates).</p>

<p>You will often hear about the <a href="http://en.wikipedia.org/wiki/Model_view_controller"
class="external-link" rel="nofollow">Model-View-Controller pattern</a> (MVC).  In
Tapestry, the page class acts as both the Model (the source of data) and the controller (the
logic that responds to user interaction).  The template is the View in MVC.  As a model, the
page exposes JavaBeans properties that can be referenced in the template.</p>

<p>Let's look at how the component template builds on the Java class to provide the
full user interface.</p>

<h1><a name="ExploringtheProject-ComponentTemplate"></a>Component Template</h1>

<p>Tapestry pages are the combination of a POJO Java class with a Tapestry component
template. The template has the same name as the Java class, but has the extension <tt>.tml</tt>.
Since the Java class here is com.example.tutorial.pages.Index, the template file will be located
at src/main/resource/com/example/tutorial/pages/Index.tml.  Ultimately, both the Java class
and the component template file will be stored in the same folder within the deployed WAR
file.</p>

<p>Tapestry component templates are well-formed XML documents. This means that you can
use any available XML editor. Templates may even have a DOCTYPE or an XML schema to validate
the structure of the template page<style type='text/css'>
.FootnoteMarker, .FootnoteNum a {
  background: transparent url(/confluence/download/resources/com.adaptavist.confluence.footnoteMacros:footnote/gfx/footnote.png)
no-repeat top right;
  padding: 1px 2px 0px 1px;
  border-left: 1px solid #8898B8;
  border-bottom: 1px solid #6B7C9B;
  margin: 1px;
  text-decoration: none;
}
.FootnoteNum a {
  margin-top: 2px;
  margin-right: 0px;
}
.FootnoteNum {
  font-size: x-small;
  text-align: right;
  padding-bottom: 4px;
}
.footnote-th1 {
  text-align: right;
}
.Footnote {
  padding-left: 7px;
  margin-bottom: 4px;
  border: 1px none #DDDDDD;
  writingMode: tb-rl;
}
.accessibility {
     display: none;
     visibility: hidden;
}
@media aural,braille,embossed {
        .FootnoteMarker, .FootnoteNum a {
         border: 1px solid #000000;
         background: #ffffff none;
    }
    .accessibility {
         display: run-in;
         visibility: visible;
    }
}
</style>
<script type='text/javascript' language='JavaScript'>
//<!--\n
var effectInProgress = {};
var despamEffect = function (id,effectType,duration) {
  if ((effectInProgress[id]) || (typeof(Effect)=="undefined") || (typeof(Effect[effectType])=="undefined"))
return;
  new Effect[effectType](id);
  effectInProgress[id]=true;
  setTimeout('effectInProgress[\"'+id+'\"]=false;',duration*1000);
};
var oldFootnoteId = '';
var footnoteHighlight = function(id,pulsateNum) {
  if (oldFootnoteId!='') document.getElementById('Footnote'+oldFootnoteId).style['borderStyle']
= 'none';
  oldFootnoteId = id;
  document.getElementById('Footnote'+id).style['borderStyle'] = 'solid';
  despamEffect('Footnote'+id,'Highlight',1)
  if (pulsateNum) despamEffect('FootnoteNum'+id,'Pulsate',3)
}
var footnoteMarkerHighlight = function(id) {
  if (oldFootnoteId!='') document.getElementById('Footnote'+oldFootnoteId).style['borderStyle']
= 'none';
  oldFootnoteId = '';
  despamEffect('FootnoteMarker'+id,'Pulsate',3)
}
//-->
</script>

<sup id='FootnoteMarker1'>
    <a name='FootnoteMarker1'
        href='#Footnote1'
        onClick='footnoteHighlight("1",true);'
        alt='Footnote: Click here to display the footnote'
        title='Footnote: Click here to display the footnote'
        class='FootnoteMarker'>
            1
    </a>
</sup>
.</p>

<p>For the most part, a Tapestry comopnent template looks like ordinary XHTML:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>src/main/resources/com/example/tutorial/pages/Index.tml</b></div><div
class="codeContent panelContent">
<pre class="code-xml">
&lt;html t:type=<span class="code-quote">"layout"</span> title=<span class="code-quote">"tutorial1
Index"</span>
      t:sidebarTitle=<span class="code-quote">"Current Time"</span>
      <span class="code-keyword">xmlns:t</span>=<span class="code-quote">"http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"</span>
      <span class="code-keyword">xmlns:p</span>=<span class="code-quote">"tapestry:parameter"</span>&gt;
        <span class="code-tag"><span class="code-comment">&lt;!-- Most of
the page content, including &lt;head&gt;</span>, <span class="code-tag">&lt;body&gt;</span>,
etc. tags, comes from Layout.tml --&gt;</span>

    <span class="code-tag">&lt;p&gt;</span>${message:greeting}<span
class="code-tag">&lt;/p&gt;</span>

    <span class="code-tag">&lt;p:sidebar&gt;</span>

        <span class="code-tag">&lt;p&gt;</span>
            Just to prove this is live:
        <span class="code-tag">&lt;/p&gt;</span>

        <span class="code-tag">&lt;p&gt;</span>The current time is: ${currentTime}.<span
class="code-tag">&lt;/p&gt;</span>


        <span class="code-tag">&lt;p&gt;</span>
            [<span class="code-tag">&lt;t:pagelink page=<span class="code-quote">"Index"</span>&gt;</span>refresh<span
class="code-tag">&lt;/t:pagelink&gt;</span>]
        <span class="code-tag">&lt;/p&gt;</span>
    <span class="code-tag">&lt;/p:sidebar&gt;</span>

<span class="code-tag">&lt;/html&gt;</span>
</pre>
</div></div>

<div class='panelMacro'><table class='tipMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/check.gif" width="16" height="16"
align="absmiddle" alt="" border="0"></td><td>You do have to name your component
template file, Index.tml, with the <b>exact same case</b> as the component class
name, Index. If you get the case wrong, it may work on some operating systems (such as Windows)
and not on others (Mac OS X, Linux, and most others). This can be really vexing, as it is
common to develop on Windows and deploy on Linux or Solaris, so be careful about case in this
one area.</td></tr></table></div>

<p>The goal in Tapestry is for component templates, such as Index.tml, to look as much
as possible like ordinary, static HTML files
<sup id='FootnoteMarker2'>
    <a name='FootnoteMarker2'
        href='#Footnote2'
        onClick='footnoteHighlight("2",true);'
        alt='Footnote: Click here to display the footnote'
        title='Footnote: Click here to display the footnote'
        class='FootnoteMarker'>
            2
    </a>
</sup>
. In fact, the expectation is that in many cases, the templates will start as static HTML
files, created by a web developer, and then be <em>instrumented</em> to act as
live Tapestry pages.</p>

<p>Tapestry hides non-standard elements and attributes inside XML namespaces. By convention,
the prefix "t:" is used for the primary namespace, but that is not a requirement, any prefix
you want to use is fine.</p>

<p>This short template demonstrates quite a few features of Tapestry.</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>Part of the concept
of the quickstart archetype is to demonstrate a bunch of different features, approaches and
common patterns used in Tapestry, thus we're hitting you with a lot all at once.</td></tr></table></div>

<p>First of all, there are two XML namespaces defined:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
  <span class="code-keyword">xmlns:t</span>=<span class="code-quote">"http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"</span>
  <span class="code-keyword">xmlns:p</span>=<span class="code-quote">"tapestry:parameter"</span>
</pre>
</div></div>

<p>The first namespace, "t:", it used to identify Tapestry-specific elements and attributes.
 Although there is an XSD (that is, a XML schema definition), it is incomplete (for reasons
explained shortly).</p>

<p>The second namespace, "p:", is a way of marking a chunk of the template as a parameter
passed into another component. We'll expand on that shortly.</p>

<p>A Tapestry component template consists mostly of standard XHTML that will pass down
to the client web browser unchanged.  The dynamic aspects of the template are represented
by <em>components</em> and <em>expansions</em>.</p>

<h1><a name="ExploringtheProject-ExpansionsinTemplates"></a>Expansions in
Templates</h1>

<p>Let's start with expansions. Expansions are an easy way of including some dynamic
output when rendering the page.  By default, an expansion refers to a JavaBeans property of
the page:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
  <span class="code-tag">&lt;p&gt;</span>The current time is: ${currentTime}<span
class="code-tag">&lt;/p&gt;</span>
</pre>
</div></div>

<div class='panelMacro'><table class='tipMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/check.gif" width="16" height="16"
align="absmiddle" alt="" border="0"></td><td>If you are coming to Tapestry
5 from Tapestry 4 or earlier, expansions are a concise replacement for the Insert component.</td></tr></table></div>

<p>The value inside the curly braces is a <em>property expression</em>.
 Tapestry uses its own property expression language that is expressive, fast, and type-safe
<sup id='FootnoteMarker3'>
    <a name='FootnoteMarker3'
        href='#Footnote3'
        onClick='footnoteHighlight("3",true);'
        alt='Footnote: Click here to display the footnote'
        title='Footnote: Click here to display the footnote'
        class='FootnoteMarker'>
            3
    </a>
</sup>
. More advanced property expressions can traverse multiple properties (for example, <tt>user.address.city</tt>),
or even invoke public methods.  Here the expansion simply reads the <tt>currentTime</tt>
property of the page.</p>

<p>Tapestry follows the rules defined by Sun's JavaBeans specification: a property name
of <tt>currentTime</tt> maps to two methods: <tt>getCurrentTime()</tt>
and <tt>setCurrentTime()</tt>. If you omit one or the other of these methods,
the property is either read only (as here), or write only
<sup id='FootnoteMarker4'>
    <a name='FootnoteMarker4'
        href='#Footnote4'
        onClick='footnoteHighlight("4",true);'
        alt='Footnote: Click here to display the footnote'
        title='Footnote: Click here to display the footnote'
        class='FootnoteMarker'>
            4
    </a>
</sup>
.</p>

<p>Tapestry does go one step further: it ignores case when matching properties inside
the expansion to properties of the page. In the template we could say ${currenttime} or ${CurrentTime}
or any variation, and Tapestry will <em>still</em> invoke the <tt>getCurrentTime()</tt>
method.</p>

<p>Note that in Tapestry it is not necessary to configure what object holds the <tt>currentTime</tt>
property; a template and a page are always used in combination with each other; expressions
are always rooted in the page instance, in this case, an instance of the Index class. </p>

<p>The Index.tml template includes a second expansion:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
    <span class="code-tag">&lt;p&gt;</span>${message:greeting}<span
class="code-tag">&lt;/p&gt;</span>
</pre>
</div></div>

<p>Here <tt>greeting</tt> is not a property of the page; its actually a
localized message key. Every Tapestry page and component is allowed to have its own message
catalog
<sup id='FootnoteMarker5'>
    <a name='FootnoteMarker5'
        href='#Footnote5'
        onClick='footnoteHighlight("5",true);'
        alt='Footnote: Click here to display the footnote'
        title='Footnote: Click here to display the footnote'
        class='FootnoteMarker'>
            5
    </a>
</sup>
.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>src/main/resources/com/example/tutorial/pages/Index.properties</b></div><div
class="codeContent panelContent">
<pre class="code-java">
greeting=Welcome to Tapestry 5!  We hope that <span class="code-keyword">this</span>
project template will get you going in style.
</pre>
</div></div>

<p>Message catalogs are useful for storing repeating strings outside of code or templates,
though their primary purpose is related to localization of the application (which will be
described in more detail in a later chapter). Messages that may be used across multiple pages
can be stored in the application's global message catalog, src/main/webapp/WEB-INF/app.properties,
instead.</p>

<p>This "message:" prefix is not some special case; there are actually quite a few of
these "binding prefixes" built into Tapestry, each  having a specific purpose. In fact, omitting
a binding prefix in an expansion is exactly the same as using the "prop:" binding prefix,
which means to treat the binding as a property expression.</p>

<p>Expansions are useful for extracting a piece of information and rendering it out
to the client as a string, but the real heavy lifting of Tapestry occurs inside components.</p>

<h1><a name="ExploringtheProject-ComponentsInsideTemplates"></a>Components
Inside Templates</h1>

<p>Components can be represented inside a component template in two ways
<sup id='FootnoteMarker6'>
    <a name='FootnoteMarker6'
        href='#Footnote6'
        onClick='footnoteHighlight("6",true);'
        alt='Footnote: Click here to display the footnote'
        title='Footnote: Click here to display the footnote'
        class='FootnoteMarker'>
            6
    </a>
</sup>
:</p>

<ul>
	<li>As an ordinary element, but with a t:type attribute to define the type of component.</li>
</ul>


<ul>
	<li>As an element in the Tapestry namespace, in which case the element name determines
the type.</li>
</ul>


<p>Here we've used an &lt;html&gt; element to represent the application's Layout
component.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
<span class="code-tag">&lt;html t:type=<span class="code-quote">"layout"</span>&gt;</span>

  ...
<span class="code-tag">&lt;/html&gt;</span>
</pre>
</div></div>

<p>But for the PageLink component, we've used an element in the Tapestry namespace:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
<span class="code-tag">&lt;t:pagelink&gt;</span> ... <span class="code-tag">&lt;/t:pagelink&gt;</span>
</pre>
</div></div>

<p>Which form you select is a matter of choice. In the vast majority of cases, they
are exactly equivalent.</p>

<p>As elsewhere, case is ignored.  Here the types ("layout" and "pagelink") were in
all lower case; the actual class names are Layout and PageLink.  Further, Tapestry "blends"
the core library components in with the components defined by this application; thus type
"layout" is mapped to application component class com.example.tutorial.components.Layout,
but "pagelink" is mapped to Tapestry's built-in org.apache.tapestry5.corelib.components.PageLink
class.</p>

<p>Tapestry components are configured using parameters; for each component, there is
a set of parameters, each with a specific type and purpose. Some parameters are required,
others are optional. Attributes of the element are used to <em>bind</em> parameters
to specific literal values, or to page properties.  Tapestry is flexible here as well; you
can always place an attribute in the Tapestry namespace (using the "t:" prefix), but in most
cases, this is unnecessary.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
&lt;html t:type=<span class="code-quote">"layout"</span> title=<span class="code-quote">"tutorial1
Index"</span>
      t:sidebarTitle=<span class="code-quote">"Current Time"</span>
</pre>
</div></div>

<p>This binds two parameters, <tt>title</tt> and <tt>sidebarTitle</tt>
of the Layout component, to the literal strings "tutorial1 Index" and "Current Time", respectively.</p>

<p>The Layout component will actually provide the bulk of the HTML ultimately sent to
the browser; we'll look at its template in a later chapter. The point is, the page's template
is integrated into the Layout component's template. The following diagram shows how parameters
passed to the Layout component end up rendered in the final page:</p>


<table width="100%">
    <tr><td align="left"> 
        <table>     
            <caption align="bottom">
                         </caption>

            <tr><td>
                            <a href="/confluence/spaces/gliffy/viewlargediagram.action?name=Templates+and+Parameters&ceoid=24188263&key=TAPESTRY&pageId=24188263"
style="outline-style: none">
                    <img style="border: none" usemap="#GLIFFY_MAP_24188263_Templates_and_Parameters"
src="/confluence/plugins/servlet/gliffyapi/clientdiagramjpeg?cb=-79392740&pk=pub&name=Templates+and+Parameters&ceoid=24188263&key=TAPESTRY&size=S&version=2"
alt="A&#32;Gliffy&#32;Diagram&#32;named&#58;&#32;Templates&#32;and&#32;Parameters"
border="none"/>
                </a>
                       </td></tr>
        </table> 
</td></tr>
</table>
<map name='GLIFFY_MAP_24188263_Templates_and_Parameters'></map>

<p>The interesting point here (and this is an advanced concept in Tapestry, one we'll
return to later) is that we can pass a chunk of the Index.tml template to the Layout component
as the <tt>sidebar</tt> parameter. That's what the tapestry:parameter namespace
(the "p:" prefix) is for; the element name is matched against a parameter of the component
and the entire block of the template is passed into the Layout component ... which decides
where, inside <em>its</em> template, that block gets rendered.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
<span class="code-tag">&lt;t:pagelink page=<span class="code-quote">"Index"</span>&gt;</span>refresh<span
class="code-tag">&lt;/t:pagelink&gt;</span>
</pre>
</div></div>

<p>This time, it's the <tt>page</tt> parameter of the PageLink component
that is bound, to the literal value "Index" (which is the name of this page). This gets rendered
as a URL that re-renders the page, which is how the current time gets updated.  You can also
create links to other pages in the application and, as we'll see in later chapters, attach
additional information to the URL beyond just the page name.</p>

<h1><a name="ExploringtheProject-AMagicTrick"></a>A Magic Trick</h1>

<p>Now it's time for a magic trick. Edit Index.java and change the <tt>getCurrentTime()</tt>
method to:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>Index.java (partial)</b></div><div
class="codeContent panelContent">
<pre class="code-java">
  <span class="code-keyword">public</span> <span class="code-object">String</span>
getCurrentTime()
  {
    <span class="code-keyword">return</span> <span class="code-quote">"A
great day to learn Tapestry"</span>;
  }
</pre>
</div></div>

<p>Make sure you save changes; then click the refresh link in the web browser:</p>

<p><span class="image-wrap" style=""><a class="confluence-thumbnail-link 1026x746"
href='https://cwiki.apache.org/confluence/download/attachments/24188263/app-live-reload.png'><img
src="/confluence/download/thumbnails/24188263/app-live-reload.png" style="border: 0px solid
black" /></a></span></p>

<p>This is one of Tapestry's early <em>wow factor</em> features: changes
to your component classes are picked up immediately. No restart. No re-deploy. Make the changes
and see them <em>now</em>. Nothing should slow you down or get in the way of you
getting your job done.</p>

<p>But ... what if you make a mistake? What if you got the name in the template wrong.
 Give it a try; in the template, change ${currentTime} to, say, ${currenTime}, and see what
you get:</p>

<p><span class="image-wrap" style=""><a class="confluence-thumbnail-link 1026x746"
href='https://cwiki.apache.org/confluence/download/attachments/24188263/app-error-1.png'><img
src="/confluence/download/thumbnails/24188263/app-error-1.png" style="border: 0px solid black"
/></a></span></p>

<p>This is Tapestry's exception report page.  It's quite detailed.  It clearly identifies
what Tapestry was doing, and relates the problem to a specific line in the template, which
is shown in context. Tapestry always expands out the entire stack of exceptions, because it
is so common for exceptions to be thrown, caught, and re-thrown inside other exceptions. In
fact, if we scroll down just a little bit, we see more detail about this exception, plus a
little bit of help:</p>

<p><span class="image-wrap" style=""><a class="confluence-thumbnail-link 1026x746"
href='https://cwiki.apache.org/confluence/download/attachments/24188263/app-error-2.png'><img
src="/confluence/download/thumbnails/24188263/app-error-2.png" style="border: 0px solid black"
/></a></span></p>

<p>This is part of Tapestry's way: it not only spells out exactly what it was doing
and what went wrong, but it even helps you find a solution; here it tells you the names of
properties you could have used.</p>

<p>Tapestry displays the stack trace of the deepest exception, along with lots of details
about the run-time environment: details about the current request, the HttpSession (if one
exists), and even a detailed list of all JVM system properties.  Scroll down to see all this
information.</p>


<div class='panelMacro'><table class='infoMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/information.gif" width="16"
height="16" align="absmiddle" alt="" border="0"></td><td>This level of detail
reflects that the application has been configured to run in <em>development mode</em>
instead of <em>production mode</em>. In production mode, the exception report
would simply be the top level exception message. However, most production applications go
further and customize how Tapestry handles and reports exceptions.</td></tr></table></div>

<hr />
<p><table class='Footnotes' style='width: 100%; border:none;' cellspacing='0' cellpadding='0'
summary='This table contains one or more notes for references made elsewhere on the page.'>
  <caption class='accessibility'>Footnotes</caption>
  <thead class='accessibility'>
    <tr class='accessibility'>
      <th class='accessibility' id='footnote-th1'>Reference</th>
      <th class='accessibility' id='footnote-th2'>Notes</th>
    </tr>
  </thead>
  <tbody>
    <tr name='Footnote1'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker1'
          onClick='footnoteMarkerHighlight("1");'
          onMouseOver='footnoteHighlight("1",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum1'>
            1
        </a>
      </td>
      <td id='Footnote1'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          Tapestry parses component templates using a non-validating parser; it only checks
for well-formedness: proper syntax, balanced elements, attribute values are quoted, and so
forth. It is reasonable for your <em>build process</em> to perform some kind of
template validation, but Tapestry accepts the template as-is, as long as it parses cleanly.

<p>      </td>
    </tr>
    <tr name='Footnote2'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker2'
          onClick='footnoteMarkerHighlight("2");'
          onMouseOver='footnoteHighlight("2",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum2'>
            2
        </a>
      </td>
      <td id='Footnote2'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          By static, we mean unchanging, as opposed to a dynamically generated Tapestry page.
      </td>
    </tr>
    <tr name='Footnote3'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker3'
          onClick='footnoteMarkerHighlight("3");'
          onMouseOver='footnoteHighlight("3",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum3'>
            3
        </a>
      </td>
      <td id='Footnote3'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          Tapestry does <em>not</em> use reflection to implement property expressions.
      </td>
    </tr>
    <tr name='Footnote4'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker4'
          onClick='footnoteMarkerHighlight("4");'
          onMouseOver='footnoteHighlight("4",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum4'>
            4
        </a>
      </td>
      <td id='Footnote4'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          Keep in mind that as far as JavaBeans properties go, it's the <em>methods</em>
that count; the names of the instance variables, or even whether they exist, is immaterial.
      </td>
    </tr>
    <tr name='Footnote5'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker5'
          onClick='footnoteMarkerHighlight("5");'
          onMouseOver='footnoteHighlight("5",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum5'>
            5
        </a>
      </td>
      <td id='Footnote5'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          There's also a global message catalog, which we'll describe later.
      </td>
    </tr>
    <tr name='Footnote6'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker6'
          onClick='footnoteMarkerHighlight("6");'
          onMouseOver='footnoteHighlight("6",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum6'>
            6
        </a>
      </td>
      <td id='Footnote6'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          Ok, there's a third way as well, which will be discussed in good time.
      </td>
    </tr>
  </tbody>
</table></p></p>

<style type='text/css'>/*<![CDATA[*/
table.ScrollbarTable  {border: none;padding: 3px;width: 100%;padding: 3px;margin: 0px;background-color:
#f0f0f0}
table.ScrollbarTable td.ScrollbarPrevIcon {text-align: center;width: 16px;border: none;}
table.ScrollbarTable td.ScrollbarPrevName {text-align: left;border: none;}
table.ScrollbarTable td.ScrollbarParent {text-align: center;border: none;}
table.ScrollbarTable td.ScrollbarNextName {text-align: right;border: none;}
table.ScrollbarTable td.ScrollbarNextIcon {text-align: center;width: 16px;border: none;}

/*]]>*/</style><div class="Scrollbar"><table class='ScrollbarTable'><tr><td
class='ScrollbarPrevIcon'><a href="/confluence/display/TAPESTRY/Loading+the+Project+Into+Eclipse"><img
border='0' align='middle' src='/confluence/images/icons/back_16.gif' width='16' height='16'></a></td><td
width='33%' class='ScrollbarPrevName'><a href="/confluence/display/TAPESTRY/Loading+the+Project+Into+Eclipse">Loading
the Project Into Eclipse</a>&nbsp;</td><td width='33%' class='ScrollbarParent'><sup><a
href="/confluence/display/TAPESTRY/Tapestry+Tutorial"><img border='0' align='middle'
src='/confluence/images/icons/up_16.gif' width='8' height='8'></a></sup><a
href="/confluence/display/TAPESTRY/Tapestry+Tutorial">Tapestry Tutorial</a></td><td
width='33%' class='ScrollbarNextName'>&nbsp;<a href="/confluence/display/TAPESTRY/Implementing+the+Hi-Lo+Guessing+Game">Implementing
the Hi-Lo Guessing Game</a></td><td class='ScrollbarNextIcon'><a href="/confluence/display/TAPESTRY/Implementing+the+Hi-Lo+Guessing+Game"><img
border='0' align='middle' src='/confluence/images/icons/forwd_16.gif' width='16' height='16'></a></td></tr></table></div>
<div class='table-wrap'>
<table class='confluenceTable'><tbody>
<tr>
</tr>
</tbody></table>
</div>

    </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/Exploring+the+Project">View
Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=24188263&revisedVersion=6&originalVersion=5">View
Changes</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message