You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by bu...@apache.org on 2014/11/23 04:20:37 UTC

svn commit: r930175 - in /websites/production/tapestry/content: ./ cache/ implementing-the-hi-lo-guessing-game.data/

Author: buildbot
Date: Sun Nov 23 03:20:37 2014
New Revision: 930175

Log:
Production update by buildbot for tapestry

Modified:
    websites/production/tapestry/content/cache/main.pageCache
    websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.data/guess-no-target-prop.png
    websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.data/guess-target-zero.png
    websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.data/guess-target.png
    websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.data/guess-template-missing.png
    websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.data/hilo-index-missing-action-error.png
    websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.html

Modified: websites/production/tapestry/content/cache/main.pageCache
==============================================================================
Binary files - no diff available.

Modified: websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.data/guess-no-target-prop.png
==============================================================================
Binary files - no diff available.

Modified: websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.data/guess-target-zero.png
==============================================================================
Binary files - no diff available.

Modified: websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.data/guess-target.png
==============================================================================
Binary files - no diff available.

Modified: websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.data/guess-template-missing.png
==============================================================================
Binary files - no diff available.

Modified: websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.data/hilo-index-missing-action-error.png
==============================================================================
Binary files - no diff available.

Modified: websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.html
==============================================================================
--- websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.html (original)
+++ websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.html Sun Nov 23 03:20:37 2014
@@ -92,12 +92,12 @@ table.ScrollbarTable td.ScrollbarNextIco
 {
 }
 ]]></script>
-</div></div><p>Running the application gives us our start:</p><p><img class="confluence-embedded-image" width="700" src="https://cwiki.apache.org/confluence/download/attachments/23340505/hilo-1.png?version=2&amp;modificationDate=1416709010700&amp;api=v2" data-image-src="/confluence/download/attachments/23340505/hilo-1.png?version=2&amp;modificationDate=1416709010700&amp;api=v2"></p><p>However, clicking the link doesn't do anything yet, as its just a placeholder \&lt;a\&gt; tag, not an actual Tapestry component. Let's think about what should happen when the user clicks that link:</p><ul><li>A random target number between 1 and 10 should be selected</li><li>The number of guesses taken should be reset to 0</li><li>The user should be sent to the Guess page to make a guess</li></ul><p>Our first step is to find out when the user clicks that "start guessing" link. In a typical web application framework, we might start thinking about URLs and handlers and maybe some sort of XML configuratio
 n file. But this is Tapestry, so we're going to work with components and methods on our classes.</p><p>First, the component. We want to perform an action (selecting the number) before continuing on to the Guess page. The ActionLink component is just what we need; it creates a link with a URL that will trigger an action event in our code ... but that's getting ahead of ourselves. First up, convert the \&lt;a\&gt; tag to an ActionLink component:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Index.tml (partial)</b></div><div class="codeContent panelContent pdl">
+</div></div><p>Running the application gives us our start:</p><p><img class="confluence-embedded-image" width="700" src="https://cwiki.apache.org/confluence/download/attachments/23340505/hilo-1.png?version=2&amp;modificationDate=1416709010700&amp;api=v2" data-image-src="/confluence/download/attachments/23340505/hilo-1.png?version=2&amp;modificationDate=1416709010700&amp;api=v2"></p><p>However, clicking the link doesn't do anything yet, as its just a placeholder &lt;a&gt; tag, not an actual Tapestry component. Let's think about what should happen when the user clicks that link:</p><ul><li>A random target number between 1 and 10 should be selected</li><li>The number of guesses taken should be reset to 0</li><li>The user should be sent to the Guess page to make a guess</li></ul><p>Our first step is to find out when the user clicks that "start guessing" link. In a typical web application framework, we might start thinking about URLs and handlers and maybe some sort of XML configuration 
 file. But this is Tapestry, so we're going to work with components and methods on our classes.</p><p>First, the component. We want to perform an action (selecting the number) before continuing on to the Guess page. The ActionLink component is just what we need; it creates a link with a URL that will trigger an action event in our code ... but that's getting ahead of ourselves. First up, convert the &lt;a&gt; tag to an ActionLink component:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Index.tml (partial)</b></div><div class="codeContent panelContent pdl">
 <script class="theme: Default; brush: xml; gutter: false" type="syntaxhighlighter"><![CDATA[  &lt;p&gt;
     &lt;t:actionlink t:id=&quot;start&quot;&gt;start guessing&lt;/t:actionlink&gt;
   &lt;/p&gt;
 ]]></script>
-</div></div><p>If you refresh the browser, you'll see that the URL for the "start guessing" link is now /tutorial1/index.start, which identifies the name of the page ("index") and the id of the component ("start").</p><p>If you click the link, you'll get an error:</p><p><img class="confluence-embedded-image" width="700" src="https://cwiki.apache.org/confluence/download/attachments/23340505/hilo-index-missing-action-error.png?version=1&amp;modificationDate=1290112782000&amp;api=v2" data-image-src="/confluence/download/attachments/23340505/hilo-index-missing-action-error.png?version=1&amp;modificationDate=1290112782000&amp;api=v2"></p><p>Tapestry is telling us that we need to provide some kind of event handler for that event. What does that look like?</p><p>An event handler is a method of the Java class with a special name. The name is <code>on</code><em>event-name</em><code>From</code><em>component-id</em> ... here we want a method named <code>onActionFromStart()</code>. How do we kn
 ow that "action" is the right event name? Because that's what ActionLink does, that's why its named _Action_Link.</p><p>Once again, Tapestry gives us options; if you don't like naming conventions, there's an @<a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/OnEvent.html">OnEvent</a> annotation you can place on the method instead, which restores the freedom to name the method as you like. Details about this approach are in the <a shape="rect" href="component-events.html">Tapestry Users' Guide</a>. We'll be sticking with the naming convention approach for the tutorial.</p><p>When handling a component event request (the kind of request triggered by the ActionLink component's URL), Tapestry will find the component and trigger a component event on it. This is the callback our server-side code needs to figure out what the user is doing on the client side. Let's start with an empty event handler:</p><div class="code pan
 el pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Index.java</b></div><div class="codeContent panelContent pdl">
+</div></div><p>If you refresh the browser and hover your mouse over the "start guessing" link, you'll see that its URL is now /tutorial1/index.start, which identifies the name of the page ("index") and the id of the component ("start").</p><p>If you click the link now, you'll get an error:</p><p><img class="confluence-embedded-image" width="700" src="https://cwiki.apache.org/confluence/download/attachments/23340505/hilo-index-missing-action-error.png?version=3&amp;modificationDate=1416709525392&amp;api=v2" data-image-src="/confluence/download/attachments/23340505/hilo-index-missing-action-error.png?version=3&amp;modificationDate=1416709525392&amp;api=v2"></p><p>Tapestry is telling us that we need to provide some kind of event handler for that event. What does that look like?</p><p>An event handler is a method of the Java class with a special name. The name is <code>on</code><strong><em>Eventname</em></strong><code>From</code><strong><em>Component-id</em></strong> ... here we want a 
 method named <code>onActionFromStart()</code>. How do we know that "action" is the right event name? Because that's what ActionLink does, that's why its named <strong><em>Action</em></strong>Link.</p><p>Once again, Tapestry gives us options; if you don't like naming conventions, there's an @<a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/OnEvent.html">OnEvent</a> annotation you can place on the method instead, which restores the freedom to name the method as you like. Details about this approach are in the <a shape="rect" href="component-events.html">Tapestry Users' Guide</a>. We'll be sticking with the naming convention approach for the tutorial.</p><p>When handling a component event request (the kind of request triggered by the ActionLink component's URL), Tapestry will find the component and trigger a component event on it. This is the callback our server-side code needs to figure out what the user is doing o
 n the client side. Let's start with an empty event handler:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Index.java</b></div><div class="codeContent panelContent pdl">
 <script class="theme: Default; brush: java; gutter: false" type="syntaxhighlighter"><![CDATA[package com.example.tutorial.pages;
 
 public class Index
@@ -108,7 +108,7 @@ public class Index
   }
 }
 ]]></script>
-</div></div><p>In the browser, we can re-try the failed component event request by hitting the refresh button ... or we can restart the application. In either case, we get the default behavior, which is simply to re-render the page.</p><p>Note that the event handler method does not have to be public; it can be protected, private, or package private (as in this example). By convention, such methods are package private, if for no other reason than it is the minimal amount of characters to type.</p><p>Hmm... right now you have to trust me that the method got invoked. That's no good ... what's a quick way to tell for sure? One way would be have the method throw an exception, but that's a bit ugly.</p><p>How about this: add the @<a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Log.html">Log</a> annotation to the method:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" s
 tyle="border-bottom-width: 1px;"><b>Index.java (partial)</b></div><div class="codeContent panelContent pdl">
+</div></div><p>In the browser, we can re-try the failed component event request by hitting the refresh button ... or we can restart the application. In either case, we get the default behavior, which is simply to re-render the page.</p><p>Note that the event handler method does not have to be public; it can be protected, private, or package private (as in this example). By convention, such methods are package private, if for no other reason than it is the minimal amount of characters to type.</p><p>Hmm... right now you have to trust us that the method got invoked. That's no good ... what's a quick way to tell for sure? One way would be have the method throw an exception, but that's a bit ugly.</p><p>How about this: add the @<a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Log.html">Log</a> annotation to the method:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" s
 tyle="border-bottom-width: 1px;"><b>Index.java (partial)</b></div><div class="codeContent panelContent pdl">
 <script class="theme: Default; brush: java; gutter: false" type="syntaxhighlighter"><![CDATA[  import org.apache.tapestry5.annotations.Log;
 
   . . .
@@ -125,7 +125,7 @@ public class Index
 [INFO] AppModule.TimingFilter Request time: 3 ms
 [INFO] AppModule.TimingFilter Request time: 5 ms
 </pre>
-</div></div><p>The @Log annotation directs Tapestry to log method entry and exit. You'll get to see any parameters passed into the method, and any return value from the method ... as well as any exception thrown from within the method. It's a powerful debugging tool. This is an example of Tapestry's meta-programming power, something we'll use quite a bit of in the tutorial.</p><p>Why do we see two requests for one click? Tapestry uses an approach based on the <a shape="rect" class="external-link" href="http://en.wikipedia.org/wiki/Post/Redirect/Get" >Post/Redirect/Get</a> pattern. In fact, Tapestry generally performs a redirect after each component event. So the first request was to process the action, and the second request was to re-render the Index page. You can see this in the browser, because the URL is still "/tutorial1" (the URL for rendering the Index page). We'll return to this in a bit.</p><p>We're ready for the next step, which involves tying together the Index and Guess 
 pages. Index will select a target number for the user to Guess, then "pass the baton" to the Guess page.</p><p>Let's start by thinking about the Guess page. It needs a variable to store the target value in, and it needs a method that the Index page can invoke, to setup that target value.</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Guess.java</b></div><div class="codeContent panelContent pdl">
+</div></div><p>The @Log annotation directs Tapestry to log method entry and exit. You'll get to see any parameters passed into the method, and any return value from the method ... as well as any exception thrown from within the method. It's a powerful debugging tool. This is an example of Tapestry's meta-programming power, something we'll use quite a bit of in the tutorial.</p><p>Why do we see two requests for one click? Tapestry uses an approach based on the <a shape="rect" class="external-link" href="http://en.wikipedia.org/wiki/Post/Redirect/Get" >Post/Redirect/Get</a> pattern. In fact, Tapestry generally performs a redirect after each component event. So the first request was to process the action, and the second request was to re-render the Index page. You can see this in the browser, because the URL is still "/tutorial1" (the URL for rendering the Index page). We'll return to this in a bit.</p><p>We're ready for the next step, which involves tying together the Index and Guess 
 pages. Index will select a target number for the user to Guess, then "pass the baton" to the Guess page.</p><p>Let's start by thinking about the Guess page. It needs a variable to store the target value in, and it needs a method that the Index page can invoke, to set up that target value.</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Guess.java</b></div><div class="codeContent panelContent pdl">
 <script class="theme: Default; brush: java; gutter: false" type="syntaxhighlighter"><![CDATA[package com.example.tutorial.pages;
 
 public class Guess
@@ -138,7 +138,7 @@ public class Guess
   }
 }
 ]]></script>
-</div></div><p>With that in mind, we can modify Index to invoke this new <code>setup()</code> method:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Index.java (revised)</b></div><div class="codeContent panelContent pdl">
+</div></div><p>Create that Guess.java file in the same folder as Index.java. Next, we can modify Index to invoke the <code>setup()</code> method of our new Guess page class:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Index.java (revised)</b></div><div class="codeContent panelContent pdl">
 <script class="theme: Default; brush: java; gutter: false" type="syntaxhighlighter"><![CDATA[package com.example.tutorial.pages;
 
 import java.util.Random;
@@ -176,7 +176,7 @@ public class Index
                             <p>When creating your own applications, make sure that the objects stored in final variables are thread safe. It seems counter-intuitive, but final variables are shared across many threads. Ordinary instance variables are not. Fortunately, the implementation of Random is, in fact, thread safe.</p>
                     </div>
     </div>
-<p>So ... let's click the link and see what we get:</p><p><img class="confluence-embedded-image" width="700" src="https://cwiki.apache.org/confluence/download/attachments/23340505/guess-template-missing.png?version=1&amp;modificationDate=1290115417000&amp;api=v2" data-image-src="/confluence/download/attachments/23340505/guess-template-missing.png?version=1&amp;modificationDate=1290115417000&amp;api=v2"></p><p>Ah! We didn't create a Guess page template. Tapestry was really expecting us to create one, so we better do so.</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>src/main/resources/com/example/tutorial/pages/Guess.tml</b></div><div class="codeContent panelContent pdl">
+<p>So ... let's click the link and see what we get:</p><p><img class="confluence-embedded-image" width="700" src="https://cwiki.apache.org/confluence/download/attachments/23340505/guess-template-missing.png?version=2&amp;modificationDate=1416710821339&amp;api=v2" data-image-src="/confluence/download/attachments/23340505/guess-template-missing.png?version=2&amp;modificationDate=1416710821339&amp;api=v2"></p><p>Ah! We didn't create a Guess page template. Tapestry was really expecting us to create one, so we better do so.</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>src/main/resources/com/example/tutorial/pages/Guess.tml</b></div><div class="codeContent panelContent pdl">
 <script class="theme: Default; brush: java; gutter: false" type="syntaxhighlighter"><![CDATA[&lt;html t:type=&quot;layout&quot; title=&quot;Guess The Number&quot;
   xmlns:t=&quot;http://tapestry.apache.org/schema/tapestry_5_3.xsd&quot;&gt;
 
@@ -186,16 +186,16 @@ public class Index
   
 &lt;/html&gt;
 ]]></script>
-</div></div><p>Hit the browser's back button, then click the "start guessing" link again. We're getting closer:</p><p><img class="confluence-embedded-image" width="700" src="https://cwiki.apache.org/confluence/download/attachments/23340505/guess-no-target-prop.png?version=1&amp;modificationDate=1290115655000&amp;api=v2" data-image-src="/confluence/download/attachments/23340505/guess-no-target-prop.png?version=1&amp;modificationDate=1290115655000&amp;api=v2"></p><p>If you scroll down, you'll see the line of the Guess.tml template that has the error. We have a field named target, but it is private and there's no corresponding property, so Tapestry was unable to access it.</p><p>We just need to write the missing JavaBeans accessor methods <code>getTarget()</code> (and <code>setTarget()</code> for good measure). Or we could let Tapestry write those methods instead:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
+</div></div><p>Hit the browser's back button, then click the "start guessing" link again. We're getting closer:</p><p><img class="confluence-embedded-image" width="700" src="https://cwiki.apache.org/confluence/download/attachments/23340505/guess-no-target-prop.png?version=2&amp;modificationDate=1416711075694&amp;api=v2" data-image-src="/confluence/download/attachments/23340505/guess-no-target-prop.png?version=2&amp;modificationDate=1416711075694&amp;api=v2"></p><p>If you scroll down, you'll see the line of the Guess.tml template that has the error. We have a field named target, but it is private and there's no corresponding property, so Tapestry was unable to access it.</p><p>We just need to write the missing JavaBeans accessor methods <code>getTarget()</code> (and <code>setTarget()</code> for good measure). Or we could let Tapestry write those methods instead:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
 <script class="theme: Default; brush: java; gutter: false" type="syntaxhighlighter"><![CDATA[  @Property
   private int target;
 ]]></script>
-</div></div><p>The @<a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Property.html">Property</a> annotation very simply directs Tapestry to write the getter and setter method for you. You only need to do this if you are going to reference the field from the template.</p><p>We are getting very close but there's one last big oddity to handle. Once you refresh the page you'll see that target is 0!</p><p><img class="confluence-embedded-image" width="700" src="https://cwiki.apache.org/confluence/download/attachments/23340505/guess-target-zero.png?version=1&amp;modificationDate=1290115961000&amp;api=v2" data-image-src="/confluence/download/attachments/23340505/guess-target-zero.png?version=1&amp;modificationDate=1290115961000&amp;api=v2"></p><p>What gives? We know it was set to at least 1 ... where did the value go?</p><p>As noted above, Tapestry sends a redirect to the client after handling the event request. That mea
 ns that the rendering of the page happens in an entirely new request. Meanwhile, at the end of each request, Tapestry wipes out the value in each instance variable. So that means that target <em>was</em> a non-zero number during the component event request ... but by the time the new page render request comes up from the web browser to render the Guess page, the value of the target field has reverted back to its default, zero.</p><p>The solution here is to mark which fields have values that should persist from one request to the next (and next, and next ...). That's what the @<a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Persist.html">Persist</a> annotation is for:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
+</div></div><p>The @<a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Property.html">Property</a> annotation very simply directs Tapestry to write the getter and setter method for you. You only need to do this if you are going to reference the field from the template.</p><p>We are getting very close but there's one last big oddity to handle. Once you refresh the page you'll see that target is 0!</p><p><img class="confluence-embedded-image" width="700" src="https://cwiki.apache.org/confluence/download/attachments/23340505/guess-target-zero.png?version=2&amp;modificationDate=1416711323804&amp;api=v2" data-image-src="/confluence/download/attachments/23340505/guess-target-zero.png?version=2&amp;modificationDate=1416711323804&amp;api=v2"></p><p>What gives? We know it was set to at least 1 ... where did the value go?</p><p>As noted above, Tapestry sends a redirect to the client after handling the event request. That mea
 ns that the rendering of the page happens in an entirely new request. Meanwhile, at the end of each request, Tapestry wipes out the value in each instance variable. So that means that target <em>was</em> a non-zero number during the component event request ... but by the time the new page render request comes up from the web browser to render the Guess page, the value of the target field has reverted back to its default, zero.</p><p>The solution here is to mark which fields have values that should persist from one request to the next (and next, and next ...). That's what the @<a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Persist.html">Persist</a> annotation is for:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
 <script class="theme: Default; brush: java; gutter: false" type="syntaxhighlighter"><![CDATA[  @Property  
   @Persist
   private int target;
 ]]></script>
-</div></div><p>This doesn't have anything to do with database persistence (that's coming up in a later chapter). It means that the value is stored in the HttpSession between requests.</p><p>Go back to the Index page and click the link again. Finally, we have a target number:</p><p><img class="confluence-embedded-image" width="700" src="https://cwiki.apache.org/confluence/download/attachments/23340505/guess-target.png?version=1&amp;modificationDate=1290116253000&amp;api=v2" data-image-src="/confluence/download/attachments/23340505/guess-target.png?version=1&amp;modificationDate=1290116253000&amp;api=v2"></p><p>That's enough for us to get started. Let's build out the Guess page, and get ready to let the user make guesses. We'll show the count of guesses, and increment that count when they make them. We'll worry about high and low and actually selecting the correct value later.</p><p>When building Tapestry pages, you sometimes start with the Java code and build the template to match, a
 nd sometime start with the template and build the Java code to match. Both approaches are valid. Here, lets start with the markup in the template, then figure out what we need in the Java code to make it work.</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Guess.tml (revised)</b></div><div class="codeContent panelContent pdl">
+</div></div><p>This doesn't have anything to do with database persistence (that's coming up in a later chapter). It means that the value is stored in the HttpSession between requests.</p><p>Go back to the Index page and click the link again. Finally, we have a target number:</p><p><img class="confluence-embedded-image" width="700" src="https://cwiki.apache.org/confluence/download/attachments/23340505/guess-target.png?version=2&amp;modificationDate=1416711521970&amp;api=v2" data-image-src="/confluence/download/attachments/23340505/guess-target.png?version=2&amp;modificationDate=1416711521970&amp;api=v2"></p><p>That's enough for us to get started. Let's build out the Guess page, and get ready to let the user make guesses. We'll show the count of guesses, and increment that count when they make them. We'll worry about high and low and actually selecting the correct value later.</p><p>When building Tapestry pages, you sometimes start with the Java code and build the template to match, a
 nd sometime start with the template and build the Java code to match. Both approaches are valid. Here, lets start with the markup in the template, then figure out what we need in the Java code to make it work.</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>Guess.tml (revised)</b></div><div class="codeContent panelContent pdl">
 <script class="theme: Default; brush: xml; gutter: false" type="syntaxhighlighter"><![CDATA[&lt;html t:type=&quot;layout&quot; title=&quot;Guess The Number&quot;
   xmlns:t=&quot;http://tapestry.apache.org/schema/tapestry_5_3.xsd&quot;
   xmlns:p=&quot;tapestry:parameter&quot;&gt;