Modified: websites/production/tapestry/content/component-events.html
==============================================================================
--- websites/production/tapestry/content/component-events.html (original)
+++ websites/production/tapestry/content/component-events.html Wed Sep 20 
12:29:16 2017
@@ -27,6 +27,16 @@
       </title>
   <link type="text/css" rel="stylesheet" href="/resources/space.css" />
 
+          <link href='/resources/highlighter/styles/shCoreCXF.css' 
rel='stylesheet' type='text/css' />
+    <link href='/resources/highlighter/styles/shThemeCXF.css' rel='stylesheet' 
type='text/css' />
+    <script src='/resources/highlighter/scripts/shCore.js' 
type='text/javascript'></script>
+          <script src='/resources/highlighter/scripts/shBrushJava.js' 
type='text/javascript'></script>
+          <script src='/resources/highlighter/scripts/shBrushXml.js' 
type='text/javascript'></script>
+          <script src='/resources/highlighter/scripts/shBrushPlain.js' 
type='text/javascript'></script>
+        <script>
+      SyntaxHighlighter.defaults['toolbar'] = false;
+      SyntaxHighlighter.all();
+    </script>
   
   <link href="/styles/style.css" rel="stylesheet" type="text/css"/>
 
@@ -67,36 +77,118 @@
       </div>
 
       <div id="content">
-                <div id="ConfluenceContent"><p><strong>Component 
events</strong> are Tapestry's way of conveying a user's interactions with the 
web page, such as clicking links and submitting forms, to designated methods in 
your page and component classes. When a component event is triggered, Tapestry 
calls the event handler method you've provided, if any, in the containing 
component's class.</p><parameter 
ac:name="style">float:right</parameter><parameter ac:name="title">Related 
Articles</parameter><parameter 
ac:name="class">aui-label</parameter><rich-text-body><parameter 
ac:name="showLabels">false</parameter><parameter 
ac:name="showSpace">false</parameter><parameter ac:name="title">Related 
Articles</parameter><parameter ac:name="cql">label = "request-processing" and 
space = currentSpace()</parameter></rich-text-body><p>Let's look at a simple 
example. Here's a portion of the template for a page (let's call it "Review") 
that lists documents and lets a user click to edit any one of th
 em.</p><parameter ac:name="language">xml</parameter><parameter 
ac:name="title">Review.tml (partial)</parameter><plain-text-body>&lt;p&gt; 
Select document to edit: &lt;/p&gt;
+                <div id="ConfluenceContent"><p><strong>Component 
events</strong> are Tapestry's way of conveying a user's interactions with the 
web page, such as clicking links and submitting forms, to designated methods in 
your page and component classes. When a component event is triggered, Tapestry 
calls the event handler method you've provided, if any, in the containing 
component's class.</p><div class="aui-label" style="float:right" title="Related 
Articles">
+
+
+
+
+
+
+
+
+<h3>Related Articles</h3>
+
+<ul class="content-by-label"><li>
+        <div>
+                <span class="icon aui-icon aui-icon-small 
aui-iconfont-page-default" title="Page">Page:</span>        </div>
+
+        <div class="details">
+                        <a  href="page-navigation.html">Page Navigation</a>
+                
+                        
+                    </div>
+    </li><li>
+        <div>
+                <span class="icon aui-icon aui-icon-small 
aui-iconfont-page-default" title="Page">Page:</span>        </div>
+
+        <div class="details">
+                        <a  href="page-life-cycle.html">Page Life Cycle</a>
+                
+                        
+                    </div>
+    </li><li>
+        <div>
+                <span class="icon aui-icon aui-icon-small 
aui-iconfont-page-default" title="Page">Page:</span>        </div>
+
+        <div class="details">
+                        <a  href="component-rendering.html">Component 
Rendering</a>
+                
+                        
+                    </div>
+    </li><li>
+        <div>
+                <span class="icon aui-icon aui-icon-small 
aui-iconfont-page-default" title="Page">Page:</span>        </div>
+
+        <div class="details">
+                        <a  href="component-events.html">Component Events</a>
+                
+                        
+                    </div>
+    </li><li>
+        <div>
+                <span class="icon aui-icon aui-icon-small 
aui-iconfont-page-default" title="Page">Page:</span>        </div>
+
+        <div class="details">
+                        <a  href="component-events-faq.html">Component Events 
FAQ</a>
+                
+                        
+                    </div>
+    </li><li>
+        <div>
+                <span class="icon aui-icon aui-icon-small 
aui-iconfont-page-default" title="Page">Page:</span>        </div>
+
+        <div class="details">
+                        <a  href="request-processing.html">Request 
Processing</a>
+                
+                        
+                    </div>
+    </li></ul>
+</div>
+
+
+<p>Let's look at a simple example. Here's a portion of the template for a page 
(let's call it "Review") that lists documents and lets a user click to edit any 
one of them.</p><div class="code panel pdl" style="border-width: 1px;"><div 
class="codeHeader panelHeader pdl" style="border-bottom-width: 
1px;"><b>Review.tml (partial)</b></div><div class="codeContent panelContent 
pdl">
+<pre class="brush: xml; gutter: false; theme: Default" 
style="font-size:12px;">&lt;p&gt; Select document to edit: &lt;/p&gt;
 &lt;t:loop source="documents" value="document"&gt;
     &lt;div&gt;
         &lt;t:actionlink" t:id="edit" context="document.id"&gt; 
${document.name} &lt;/t:actionlink&gt;       
     &lt;/div&gt;
 &lt;/t:loop&gt;
-</plain-text-body><p>Notice that Review.tml contains an ActionLink component 
in a loop. For each rendering within the loop, the ActionLink component creates 
a component event request URL, with the event type set to "action". In this 
case, each URL might look like:</p><p><code><span 
class="external-link">&#160;&#160;&#160; 
http://localhost:8080/review.edit/3</span></code></p><p>This URL identifies the 
<strong>page</strong> that contains the component ("review"), 
the&#160;<strong>Component id</strong> of the component within the page 
("edit"), and the <strong>context</strong> value ("3", the "id" property of the 
document). <em>Additional context values, if any, are appended to the 
path.</em> (The URL may also contain the <strong>event name</strong>, unless, 
as here, it is "action".)</p><p>There's no direct mapping from URL to a piece 
of code. Instead, when the user clicks on the link, the ActionLink component 
triggers events. And then Tapestry ensures that the correct bits of code (yo
 ur event handler method, see below) get invoked for those events.</p><p>This 
demonstrates a critical difference between Tapestry and a more traditional, 
action oriented framework. The URL doesn't say what happens when the link is 
clicked, it identifies <em>which component is responsible</em> when the link is 
clicked.</p><p>Often, a navigation request (originating with the user) will 
spawn a number of flow-of-control requests. For example, a form component may 
trigger an action event, which will then trigger notification events to 
announce when the form submission is about to be processed, and whether it was 
successful or not, and those event could be further handled by the page 
component.</p><h1 id="ComponentEvents-EventHandlerMethods">Event Handler 
Methods</h1><p>When a component event occurs, Tapestry invokes any event 
handler methods that you have identified for that event. You can identify your 
event handler methods via a naming convention (see Method Naming Convention 
below), o
 r via the @<a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/OnEvent.html";>OnEvent</a>
 annotation.</p><parameter 
ac:name="language">java</parameter><plain-text-body>@OnEvent(component="edit")
+</pre>
+</div></div><p>Notice that Review.tml contains an ActionLink component in a 
loop. For each rendering within the loop, the ActionLink component creates a 
component event request URL, with the event type set to "action". In this case, 
each URL might look like:</p><p><code><span 
class="external-link">&#160;&#160;&#160; 
http://localhost:8080/review.edit/3</span></code></p><p>This URL identifies the 
<strong>page</strong> that contains the component ("review"), 
the&#160;<strong>Component id</strong> of the component within the page 
("edit"), and the <strong>context</strong> value ("3", the "id" property of the 
document). <em>Additional context values, if any, are appended to the 
path.</em> (The URL may also contain the <strong>event name</strong>, unless, 
as here, it is "action".)</p><p>There's no direct mapping from URL to a piece 
of code. Instead, when the user clicks on the link, the ActionLink component 
triggers events. And then Tapestry ensures that the correct bits of code (your 
eve
 nt handler method, see below) get invoked for those events.</p><p>This 
demonstrates a critical difference between Tapestry and a more traditional, 
action oriented framework. The URL doesn't say what happens when the link is 
clicked, it identifies <em>which component is responsible</em> when the link is 
clicked.</p><p>Often, a navigation request (originating with the user) will 
spawn a number of flow-of-control requests. For example, a form component may 
trigger an action event, which will then trigger notification events to 
announce when the form submission is about to be processed, and whether it was 
successful or not, and those event could be further handled by the page 
component.</p><h1 id="ComponentEvents-EventHandlerMethods">Event Handler 
Methods</h1><p>When a component event occurs, Tapestry invokes any event 
handler methods that you have identified for that event. You can identify your 
event handler methods via a naming convention (see Method Naming Convention 
below), or via 
 the @<a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/OnEvent.html";>OnEvent</a>
 annotation.</p><div class="code panel pdl" style="border-width: 1px;"><div 
class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">@OnEvent(component="edit")
 void editDocument(int docId)
 {
     this.selectedId = docId;
     // do something with the document here
 }
-</plain-text-body><p>Tapestry does two things here:</p><ul><li>Because of the 
annotation, it identifies method editDocument() as the method to invoke 
whenever the component whose ID is "edit" triggers an event.</li><li>Because 
there is a method parameter, when the link is clicked the context value of the 
request is converted from a string to an integer and passed in as the method's 
value parameter.<br clear="none"><br clear="none"></li></ul>
+</pre>
+</div></div><p>Tapestry does two things here:</p><ul><li>Because of the 
annotation, it identifies method editDocument() as the method to invoke 
whenever the component whose ID is "edit" triggers an event.</li><li>Because 
there is a method parameter, when the link is clicked the context value of the 
request is converted from a string to an integer and passed in as the method's 
value parameter.<br clear="none"><br clear="none"></li></ul>
 
 <div class="confluence-information-macro 
confluence-information-macro-information"><p class="title">Added in 
5.3</p><span class="aui-icon aui-icon-small aui-iconfont-info 
confluence-information-macro-icon"></span><div 
class="confluence-information-macro-body">
 </div></div>
 <div class="error"><span class="error">Unknown macro: {div}</span> 
 <p>Starting in release 5.3, Tapestry will throw an exception if the component 
identified for the event handler method doesn't exist in the containing 
component's template. This helps prevent typos.</p>
-</div><p>In the above example, the editDocument() method will be invoked when 
any event occurs in in the "edit" component (and has at least one context 
value).</p><p>For some components, more than one type of event can occur, in 
which case you will want to be more specific:</p><parameter 
ac:name="language">java</parameter><plain-text-body>@OnEvent(value="action", 
component="edit")
+</div><p>In the above example, the editDocument() method will be invoked when 
any event occurs in in the "edit" component (and has at least one context 
value).</p><p>For some components, more than one type of event can occur, in 
which case you will want to be more specific:</p><div class="code panel pdl" 
style="border-width: 1px;"><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">@OnEvent(value="action", component="edit")
 void editDocument(int docId)
 {
     this.selectedId = docId;
     // do something with the document here
 }
-</plain-text-body><p>For the OnEvent annotation, the 
<em><code>value</code></em> attribute identifies the name of the event to 
match. We specified "action" because the ActionLink component triggers the 
"action" event, as noted in the Component Events section of its <a  
class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/ActionLink.html";>javadocs</a>.</p><p>Alternatively,
 we can use the EventLink component, in which case the name of the event is 
determined by us &#8211; either through the "event" parameter or the element's 
ID:</p><parameter ac:name="language">xml</parameter><parameter 
ac:name="title">An EventLink that emits the "delete" 
event</parameter><plain-text-body>&lt;t:eventlink event="delete" 
context="document.id"&gt; ${document.name} 
&lt;/t:eventlink&gt;</plain-text-body><p>which is equivalent to:</p><parameter 
ac:name="language">xml</parameter><parameter ac:name="title">An EventLink that 
emits the "delete" event</pa
 rameter><plain-text-body>&lt;a t:type="eventlink" t:id="delete" 
context="document.id"&gt; ${document.name} &lt;/a&gt;</plain-text-body><p>Note 
that if you omit the <code>component</code> part of the OnEvent annotation, 
then you'll receive notifications from <em>all</em> contained components, 
possibly including nested components (due to event 
bubbling).</p><rich-text-body><p>You should usually specify exactly which 
component(s) you wish to receive events from. Using @OnEvent on a method and 
not specifying a specific component ID means that the method will be invoked 
for events from <em>any</em> component.</p></rich-text-body><p>To support 
testing, it's a common practice to give event handler methods 
<em>package-private</em> visibility, as in the examples on this page, although 
technically they may have any visibility (even private).</p><p>A single event 
handler method may receive notifications from many different 
components.</p><p>As elsewhere, the comparison of event type and compon
 ent ID is case-insensitive.</p><h2 
id="ComponentEvents-MethodNamingConvention">Method Naming Convention</h2><p>As 
an alternative to the use of annotations, you may name your event handling 
methods following a certain convention, and Tapestry will find and invoke your 
methods just as if they were annotated.</p><p>This style of event handler 
methods start with the prefix "on", followed by the name of the event. You may 
then continue by adding "From" and a capitalized component id (remember that 
Tapestry is case insensitive about event names and component IDs). So, for 
example, a method named onValidateFromSave() will be invoked whenever a 
"Validate" event is triggered by a component whose component ID is 
"save".</p><p>The previous example may be rewritten as:</p><parameter 
ac:name="language">java</parameter><plain-text-body>void onActionFromEdit(int 
docId)
+</pre>
+</div></div><p>For the OnEvent annotation, the <em><code>value</code></em> 
attribute identifies the name of the event to match. We specified "action" 
because the ActionLink component triggers the "action" event, as noted in the 
Component Events section of its <a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/ActionLink.html";>javadocs</a>.</p><p>Alternatively,
 we can use the EventLink component, in which case the name of the event is 
determined by us &#8211; either through the "event" parameter or the element's 
ID:</p><div class="code panel pdl" style="border-width: 1px;"><div 
class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>An 
EventLink that emits the "delete" event</b></div><div class="codeContent 
panelContent pdl">
+<pre class="brush: xml; gutter: false; theme: Default" 
style="font-size:12px;">&lt;t:eventlink event="delete" 
context="document.id"&gt; ${document.name} &lt;/t:eventlink&gt;</pre>
+</div></div><p>which is equivalent to:</p><div class="code panel pdl" 
style="border-width: 1px;"><div class="codeHeader panelHeader pdl" 
style="border-bottom-width: 1px;"><b>An EventLink that emits the "delete" 
event</b></div><div class="codeContent panelContent pdl">
+<pre class="brush: xml; gutter: false; theme: Default" 
style="font-size:12px;">&lt;a t:type="eventlink" t:id="delete" 
context="document.id"&gt; ${document.name} &lt;/a&gt;</pre>
+</div></div><p>Note that if you omit the <code>component</code> part of the 
OnEvent annotation, then you'll receive notifications from <em>all</em> 
contained components, possibly including nested components (due to event 
bubbling).</p><div class="confluence-information-macro 
confluence-information-macro-tip"><span class="aui-icon aui-icon-small 
aui-iconfont-approve confluence-information-macro-icon"></span><div 
class="confluence-information-macro-body"><p>You should usually specify exactly 
which component(s) you wish to receive events from. Using @OnEvent on a method 
and not specifying a specific component ID means that the method will be 
invoked for events from <em>any</em> component.</p></div></div><p>To support 
testing, it's a common practice to give event handler methods 
<em>package-private</em> visibility, as in the examples on this page, although 
technically they may have any visibility (even private).</p><p>A single event 
handler method may receive notifications from many dif
 ferent components.</p><p>As elsewhere, the comparison of event type and 
component ID is case-insensitive.</p><h2 
id="ComponentEvents-MethodNamingConvention">Method Naming Convention</h2><p>As 
an alternative to the use of annotations, you may name your event handling 
methods following a certain convention, and Tapestry will find and invoke your 
methods just as if they were annotated.</p><p>This style of event handler 
methods start with the prefix "on", followed by the name of the event. You may 
then continue by adding "From" and a capitalized component id (remember that 
Tapestry is case insensitive about event names and component IDs). So, for 
example, a method named onValidateFromSave() will be invoked whenever a 
"Validate" event is triggered by a component whose component ID is 
"save".</p><p>The previous example may be rewritten as:</p><div class="code 
panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">void onActionFromEdit(int docId)
 {
     this.selectedId = docId;
     // do something with the document here
 }
-</plain-text-body><rich-text-body><p>Many people prefer the naming convention 
approach, reserving the annotation just for situations that don't otherwise 
fit.</p></rich-text-body><h2 id="ComponentEvents-MethodReturnValues">Method 
Return Values</h2><p>Main Article: <a  href="page-navigation.html">Page 
Navigation</a></p><p>For page navigation events (originating in components such 
as EventLink, ActionLink and Form), the value returned from an event handler 
method determines how Tapestry will render a 
response.</p><ul><li><strong>Null</strong>: For no value, or null, the current 
page (the page containing the component) will render the 
response.</li><li><strong>Page</strong>: For the name of a page, or a page 
class or page instance, a render request URL will be constructed and sent to 
the client as a redirect to that page.</li><li><strong>URL</strong>: For a 
java.net.URL, a redirect will be sent to the client. (In Tapestry 5.3.x and 
earlier, this only works for non-Ajax requests.)</li><
 li><strong>Zone body</strong>: In the case of an Ajax request to update a 
zone, the component event handler will return the new zone body, typically via 
an injected component or block.</li><li><strong>HttpError</strong>: For an <a  
class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/HttpError.html";>HttpError</a>,
 an error response is sent to the client.</li><li><strong>Link</strong>: For a 
<a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/Link.html";>Link</a>,
 a redirect is sent to the client.</li><li><strong>Stream</strong>: For a <a  
class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/StreamResponse.html";>StreamResponse</a>,
 a stream of data is sent to the client</li><li><strong>boolean:</strong> 
<em>true</em> prevents the event from bubbling up further; <em>false</em> lets 
it bubble up. See Event Bubbling, below.</li></ul><p>See <a  href="page-navi
 gation.html">Page Navigation</a> for more details.</p><h2 
id="ComponentEvents-MultipleMethodMatches">Multiple Method Matches</h2><p>In 
some cases, there may be multiple event handler methods matching a single 
event. In that case, Tapestry invokes them in the following 
order:</p><ul><li>Base class methods before sub-class methods.</li><li>Matching 
methods within a class in alphabetical order.</li><li>For a single method name 
with multiple overrides, by number of parameters, descending.</li></ul><p>Of 
course, ordinarily would you <em>not</em> want to create more than one method 
to handle an event.</p><p>When a sub-class overrides an event handler method of 
a base class, the event handler method is only invoked once, along with any 
other base class methods. The subclass can change the <em>implementation</em> 
of the base class method via an override, but can't change the <em>timing</em> 
of when that method is invoked. See <a  class="external-link" 
href="https://issues.apache.org/jira/br
 owse/TAP5-51">issue TAP5-51</a>.</p><h2 
id="ComponentEvents-EventContext">Event Context</h2><p>The context values (the 
context parameter to the EventLink or ActionLink component) can be any object. 
However, only a simple conversion to string occurs. (This is in contrast to 
Tapestry 4, which had an elaborate type mechanism with the odd name 
"DataSqueezer".)</p><p>Again, whatever your value is (string, number, date), it 
is converted into a plain string. This results in a more readable URL.</p><p>If 
you have multiple context values (by binding a list or array of objects to the 
<em>context</em> parameter of the EventLink or ActionLink), then each one, in 
order, will be added to the URL.</p><p>When an event handler method is invoked, 
the strings are converted back into values, or even objects. A <a  
class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ValueEncoder.html";>ValueEncoder</a>
 is used to convert between client-side strings and server-side 
 objects. The <a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/ValueEncoderSource.html";>ValueEncoderSource</a>
 service provides the necessary value encoders.</p><p>As shown in the example 
above, most of the parameters passed to the event handler method are derived 
from the values provided in the event context. Each successive method parameter 
matches against a value provided in the event context (the context parameter of 
the ActionLink component; though many components have a similar context 
parameter).</p><p>In many cases it is helpful to have direct access to the 
context (for example, to adapt to cases where there are a variable number of 
context values). The context values may be passed to an event handler method as 
parameter of the following types:</p><ul><li><a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/EventContext.html";>EventContext</a></li><li>Object[]</li><li>List&lt;Obj
 ect&gt;</li></ul><p>The latter two should be avoided, they may be removed in a 
future release. In all of these cases, the context parameter acts as a freebie; 
it doesn't match against a context value as it represents <em>all</em> context 
values.</p><plain-text-body>Object onActionFromEdit(EventContext context)
+</pre>
+</div></div><div class="confluence-information-macro 
confluence-information-macro-information"><span class="aui-icon aui-icon-small 
aui-iconfont-info confluence-information-macro-icon"></span><div 
class="confluence-information-macro-body"><p>Many people prefer the naming 
convention approach, reserving the annotation just for situations that don't 
otherwise fit.</p></div></div><h2 
id="ComponentEvents-MethodReturnValues">Method Return Values</h2><p>Main 
Article: <a  href="page-navigation.html">Page Navigation</a></p><p>For page 
navigation events (originating in components such as EventLink, ActionLink and 
Form), the value returned from an event handler method determines how Tapestry 
will render a response.</p><ul><li><strong>Null</strong>: For no value, or 
null, the current page (the page containing the component) will render the 
response.</li><li><strong>Page</strong>: For the name of a page, or a page 
class or page instance, a render request URL will be constructed and sent to 
the c
 lient as a redirect to that page.</li><li><strong>URL</strong>: For a 
java.net.URL, a redirect will be sent to the client. (In Tapestry 5.3.x and 
earlier, this only works for non-Ajax requests.)</li><li><strong>Zone 
body</strong>: In the case of an Ajax request to update a zone, the component 
event handler will return the new zone body, typically via an injected 
component or block.</li><li><strong>HttpError</strong>: For an <a  
class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/HttpError.html";>HttpError</a>,
 an error response is sent to the client.</li><li><strong>Link</strong>: For a 
<a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/Link.html";>Link</a>,
 a redirect is sent to the client.</li><li><strong>Stream</strong>: For a <a  
class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/StreamResponse.html";>StreamResponse</a>,
 a stream of data is sent to 
 the client</li><li><strong>boolean:</strong> <em>true</em> prevents the event 
from bubbling up further; <em>false</em> lets it bubble up. See Event Bubbling, 
below.</li></ul><p>See <a  href="page-navigation.html">Page Navigation</a> for 
more details.</p><h2 id="ComponentEvents-MultipleMethodMatches">Multiple Method 
Matches</h2><p>In some cases, there may be multiple event handler methods 
matching a single event. In that case, Tapestry invokes them in the following 
order:</p><ul><li>Base class methods before sub-class methods.</li><li>Matching 
methods within a class in alphabetical order.</li><li>For a single method name 
with multiple overrides, by number of parameters, descending.</li></ul><p>Of 
course, ordinarily would you <em>not</em> want to create more than one method 
to handle an event.</p><p>When a sub-class overrides an event handler method of 
a base class, the event handler method is only invoked once, along with any 
other base class methods. The subclass can change the <em>
 implementation</em> of the base class method via an override, but can't change 
the <em>timing</em> of when that method is invoked. See <a  
class="external-link" 
href="https://issues.apache.org/jira/browse/TAP5-51";>issue TAP5-51</a>.</p><h2 
id="ComponentEvents-EventContext">Event Context</h2><p>The context values (the 
context parameter to the EventLink or ActionLink component) can be any object. 
However, only a simple conversion to string occurs. (This is in contrast to 
Tapestry 4, which had an elaborate type mechanism with the odd name 
"DataSqueezer".)</p><p>Again, whatever your value is (string, number, date), it 
is converted into a plain string. This results in a more readable URL.</p><p>If 
you have multiple context values (by binding a list or array of objects to the 
<em>context</em> parameter of the EventLink or ActionLink), then each one, in 
order, will be added to the URL.</p><p>When an event handler method is invoked, 
the strings are converted back into values, or even object
 s. A <a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ValueEncoder.html";>ValueEncoder</a>
 is used to convert between client-side strings and server-side objects. The <a 
 class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/ValueEncoderSource.html";>ValueEncoderSource</a>
 service provides the necessary value encoders.</p><p>As shown in the example 
above, most of the parameters passed to the event handler method are derived 
from the values provided in the event context. Each successive method parameter 
matches against a value provided in the event context (the context parameter of 
the ActionLink component; though many components have a similar context 
parameter).</p><p>In many cases it is helpful to have direct access to the 
context (for example, to adapt to cases where there are a variable number of 
context values). The context values may be passed to an event handler method as 
parameter of 
 the following types:</p><ul><li><a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/EventContext.html";>EventContext</a></li><li>Object[]</li><li>List&lt;Object&gt;</li></ul><p>The
 latter two should be avoided, they may be removed in a future release. In all 
of these cases, the context parameter acts as a freebie; it doesn't match 
against a context value as it represents <em>all</em> context values.</p><div 
class="code panel pdl" style="border-width: 1px;"><div class="codeContent 
panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">Object onActionFromEdit(EventContext context)
 {
     if (context.getCount() &gt; 0) {
         this.selectedId = context.get(0);
@@ -105,7 +197,9 @@ void editDocument(int docId)
         alertManager.warn("Please select a document.");
         return null;
     }
-}</plain-text-body><h2 
id="ComponentEvents-AccessingRequestQueryParameters">Accessing Request Query 
Parameters</h2><p>A parameter may be annotated with the @RequestParameter 
annotation; this allows query parameters (?name1=value1&amp;name2=value2, etc) 
to be extracted from the request, converted to the correct type, and passed to 
the method. Again, this doesn't count against the event context 
values.</p><p>See the example in the <a  href="link-components-faq.html">Link 
Components FAQ</a>.</p><h2 id="ComponentEvents-MethodMatching">Method 
Matching</h2><p>An event handler method will only be invoked <em>if the context 
contains at least as many values as the method has parameters</em>. Methods 
with too many parameters will be silently skipped.</p><p>Tapestry will silently 
skip over a method if there are insufficient values in the context to satisfy 
the number of parameters requested.</p><p>EventContext parameters, and 
parameters annotated with @RequestParameter, do not count against th
 is limit.</p><h2 id="ComponentEvents-MethodOrdering">Method 
Ordering</h2><p>When multiple methods match within the same class, Tapestry 
will invoke them in ascending alphabetical order. When there are multiple 
overrides of the same method name, Tapestry invokes them in descending order by 
number of parameters. In general, these situations don't happen ... in most 
cases, only a single method is required to handle a specific event form a 
specific component.</p><p>An event handler method may return the value 
<code>true</code> to indicate that the event has been handled; this immediately 
stops the search for additional methods in the same class (or in base classes) 
or in containing components.</p><h1 id="ComponentEvents-EventBubbling">Event 
Bubbling</h1><p>The event will bubble up the component hierarchy, first to the 
containing component, then <em>that</em> component's containing component or 
page, and so on, until it is <em>aborted</em> by an event handler method 
returning <em>true</e
 m> or a non-null value.</p><p>Returning a boolean value from an event handler 
method is special. Returning <em>true</em> will abort the event with no result; 
use this when the event is fully handled without a return value and no further 
event handlers (in the same component, or in containing components) should be 
invoked.</p><p>Returning <em>false</em> is the same as returning null; event 
processing will continue to look for more event handlers, in the same component 
or its parent.</p><p>When an event bubbles up from a component to its 
container, the origin of the event is changed to be the component. For example, 
a Form component inside a BeanEditForm component may trigger a success event. 
The page containing the BeanEditForm may listen for that event, but it will be 
from the BeanEditForm component (which makes sense, because the id of the Form 
inside the BeanEditForm is part of the BeanEditForm's implementation, not its 
public interface).</p><p>If you want to handle events that ha
 ve bubbled up from nested component, you'll soon find that you don't have easy 
access to the component ID of the firing component. In practical terms this 
means that you'll want to trigger custom events for the events triggered by 
those nested components (see Triggering Events, below), and use that custom 
event name in your event handler method.</p><h1 
id="ComponentEvents-EventMethodExceptions">Event Method Exceptions</h1><p>Event 
methods are allowed to throw any exception (not just runtime exceptions). If an 
event method does throw an exception, Tapestry will catch the thrown exception 
and ultimately display the exception report page.</p><p>In other words, there's 
no need to do this:</p><parameter 
ac:name="language">java</parameter><plain-text-body>  void 
onActionFromRunQuery()
+}</pre>
+</div></div><h2 id="ComponentEvents-AccessingRequestQueryParameters">Accessing 
Request Query Parameters</h2><p>A parameter may be annotated with the 
@RequestParameter annotation; this allows query parameters 
(?name1=value1&amp;name2=value2, etc) to be extracted from the request, 
converted to the correct type, and passed to the method. Again, this doesn't 
count against the event context values.</p><p>See the example in the <a  
href="link-components-faq.html">Link Components FAQ</a>.</p><h2 
id="ComponentEvents-MethodMatching">Method Matching</h2><p>An event handler 
method will only be invoked <em>if the context contains at least as many values 
as the method has parameters</em>. Methods with too many parameters will be 
silently skipped.</p><p>Tapestry will silently skip over a method if there are 
insufficient values in the context to satisfy the number of parameters 
requested.</p><p>EventContext parameters, and parameters annotated with 
@RequestParameter, do not count against this limi
 t.</p><h2 id="ComponentEvents-MethodOrdering">Method Ordering</h2><p>When 
multiple methods match within the same class, Tapestry will invoke them in 
ascending alphabetical order. When there are multiple overrides of the same 
method name, Tapestry invokes them in descending order by number of parameters. 
In general, these situations don't happen ... in most cases, only a single 
method is required to handle a specific event form a specific 
component.</p><p>An event handler method may return the value <code>true</code> 
to indicate that the event has been handled; this immediately stops the search 
for additional methods in the same class (or in base classes) or in containing 
components.</p><h1 id="ComponentEvents-EventBubbling">Event Bubbling</h1><p>The 
event will bubble up the component hierarchy, first to the containing 
component, then <em>that</em> component's containing component or page, and so 
on, until it is <em>aborted</em> by an event handler method returning 
<em>true</em> or a
  non-null value.</p><p>Returning a boolean value from an event handler method 
is special. Returning <em>true</em> will abort the event with no result; use 
this when the event is fully handled without a return value and no further 
event handlers (in the same component, or in containing components) should be 
invoked.</p><p>Returning <em>false</em> is the same as returning null; event 
processing will continue to look for more event handlers, in the same component 
or its parent.</p><p>When an event bubbles up from a component to its 
container, the origin of the event is changed to be the component. For example, 
a Form component inside a BeanEditForm component may trigger a success event. 
The page containing the BeanEditForm may listen for that event, but it will be 
from the BeanEditForm component (which makes sense, because the id of the Form 
inside the BeanEditForm is part of the BeanEditForm's implementation, not its 
public interface).</p><p>If you want to handle events that have bubb
 led up from nested component, you'll soon find that you don't have easy access 
to the component ID of the firing component. In practical terms this means that 
you'll want to trigger custom events for the events triggered by those nested 
components (see Triggering Events, below), and use that custom event name in 
your event handler method.</p><h1 
id="ComponentEvents-EventMethodExceptions">Event Method Exceptions</h1><p>Event 
methods are allowed to throw any exception (not just runtime exceptions). If an 
event method does throw an exception, Tapestry will catch the thrown exception 
and ultimately display the exception report page.</p><p>In other words, there's 
no need to do this:</p><div class="code panel pdl" style="border-width: 
1px;"><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">  void onActionFromRunQuery()
   {
     try
     {
@@ -116,23 +210,27 @@ void editDocument(int docId)
       throw new RuntimeException(ex);
     }
   }
-</plain-text-body><p>Instead, you may simply say:</p><parameter 
ac:name="language">java</parameter><plain-text-body>  void 
onActionFromRunQuery() throws JDBCException
+</pre>
+</div></div><p>Instead, you may simply say:</p><div class="code panel pdl" 
style="border-width: 1px;"><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">  void onActionFromRunQuery() throws JDBCException
   {
     dao.executeQuery();
   }
-</plain-text-body><p>Your event handler method may even declare that it 
"throws Exception" if that is more convenient.</p><h1 
id="ComponentEvents-InterceptingEventExceptions">Intercepting Event 
Exceptions</h1><p>When an event handler method throws an exception (checked or 
runtime), Tapestry gives the component and its containing page a chance to 
handle the exception, before continuing on to report the 
exception.<plain-text-body>{float:right|background=#eee|padding=0 1em}
-    *JumpStart Demo:*
-    [Handling A Bad 
Context|http://jumpstart.doublenegative.com.au/jumpstart/examples/infrastructure/handlingabadcontext/1]
-{float}</plain-text-body>Tapestry triggers a new event, of type "exception", 
passing the thrown exception as the context. In fact, the exception is wrapped 
inside a <a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/runtime/ComponentEventException.html";>ComponentEventException</a>,
 from which you may extract the event type and 
context.</p><p>Thus:</p><parameter 
ac:name="language">java</parameter><plain-text-body>  Object 
onException(Throwable cause)
+</pre>
+</div></div><p>Your event handler method may even declare that it "throws 
Exception" if that is more convenient.</p><h1 
id="ComponentEvents-InterceptingEventExceptions">Intercepting Event 
Exceptions</h1><p>When an event handler method throws an exception (checked or 
runtime), Tapestry gives the component and its containing page a chance to 
handle the exception, before continuing on to report the exception.</p><div 
class="navmenu" style="float:right; background:#eee; margin:3px; padding:0 1em">
+<p>    <strong>JumpStart Demo:</strong><br clear="none">
+    <a  class="external-link" 
href="http://jumpstart.doublenegative.com.au/jumpstart/examples/infrastructure/handlingabadcontext/1";
 rel="nofollow">Handling A Bad Context</a></p></div>Tapestry triggers a new 
event, of type "exception", passing the thrown exception as the context. In 
fact, the exception is wrapped inside a <a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/runtime/ComponentEventException.html";>ComponentEventException</a>,
 from which you may extract the event type and context.<p>Thus:</p><div 
class="code panel pdl" style="border-width: 1px;"><div class="codeContent 
panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">  Object onException(Throwable cause)
   {
     message = cause.getMessage();
 
     return this;
   }
-</plain-text-body><p>The return value of the exception event handler 
<em>replaces</em> the return value of original event handler method. For the 
typical case (an exception thrown by an "activate" or "action" event), this 
will be a <a  href="page-navigation.html">navigational response</a> such as a 
page instance or page name.</p><p>This can be handy for handling cases where 
the data in the URL is incorrectly formatted.</p><p>In the above example, the 
navigational response is the page itself.</p><p>If there is no exception event 
handler, or the exception event handler returns null (or is void), then the 
exception will be passed to the <a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/RequestExceptionHandler.html";>RequestExceptionHandler</a>
 service, which (in the default configuration) will render the exception 
page.</p><h1 id="ComponentEvents-TriggeringEvents">Triggering 
Events</h1><p><plain-text-body>{float:right|background=#ee
 e|padding=0 1em}
-    *JumpStart Demo:*
-    [AJAX Components 
CRUD|http://jumpstart.doublenegative.com.au/jumpstart/together/ajaxcomponentscrud/persons]
-{float}</plain-text-body>If you want your own component to trigger events, 
just call the <a  rel="nofollow">triggerEvent</a> method of <a  
class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ComponentResources.html";>ComponentResources</a>
 from within the component class.</p><p>For example, the following triggers an 
"updateAll" event. A containing component can then respond to it, if desired, 
with an "onUpdateAll()" method in its own component class.</p><parameter 
ac:name="language">java</parameter><parameter ac:name="title">Your component 
class (partial)</parameter><plain-text-body>@Inject
+</pre>
+</div></div><p>The return value of the exception event handler 
<em>replaces</em> the return value of original event handler method. For the 
typical case (an exception thrown by an "activate" or "action" event), this 
will be a <a  href="page-navigation.html">navigational response</a> such as a 
page instance or page name.</p><p>This can be handy for handling cases where 
the data in the URL is incorrectly formatted.</p><p>In the above example, the 
navigational response is the page itself.</p><p>If there is no exception event 
handler, or the exception event handler returns null (or is void), then the 
exception will be passed to the <a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/RequestExceptionHandler.html";>RequestExceptionHandler</a>
 service, which (in the default configuration) will render the exception 
page.</p><h1 id="ComponentEvents-TriggeringEvents">Triggering 
Events</h1><p></p><div class="navmenu" style="float:right; backg
 round:#eee; margin:3px; padding:0 1em">
+<p>    <strong>JumpStart Demo:</strong><br clear="none">
+    <a  class="external-link" 
href="http://jumpstart.doublenegative.com.au/jumpstart/together/ajaxcomponentscrud/persons";
 rel="nofollow">AJAX Components CRUD</a></p></div>If you want your own 
component to trigger events, just call the <a  rel="nofollow">triggerEvent</a> 
method of <a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ComponentResources.html";>ComponentResources</a>
 from within the component class.<p>For example, the following triggers an 
"updateAll" event. A containing component can then respond to it, if desired, 
with an "onUpdateAll()" method in its own component class.</p><div class="code 
panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" 
style="border-bottom-width: 1px;"><b>Your component class 
(partial)</b></div><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">@Inject
 ComponentResources componentResources;
 &#160;...
 private void timeToUpdate() {
@@ -140,7 +238,8 @@ private void timeToUpdate() {
     if (wasHandled) {
         ...
     }
-} </plain-text-body><p>The third parameter to triggerEvent is a 
ComponentEventCallback, which you'll only need to implement if you want to get 
the return value of the handler method. The return value of <span 
class="il">triggerEvent</span>() says if the <span class="il">event</span> was 
handled or not.</p><p>&#160;</p></div>
+} </pre>
+</div></div><p>The third parameter to triggerEvent is a 
ComponentEventCallback, which you'll only need to implement if you want to get 
the return value of the handler method. The return value of <span 
class="il">triggerEvent</span>() says if the <span class="il">event</span> was 
handled or not.</p><p>&#160;</p></div>
       </div>
 
       <div class="clearer"></div>

Modified: websites/production/tapestry/content/component-libraries.html
==============================================================================
--- websites/production/tapestry/content/component-libraries.html (original)
+++ websites/production/tapestry/content/component-libraries.html Wed Sep 20 
12:29:16 2017
@@ -27,6 +27,15 @@
       </title>
   <link type="text/css" rel="stylesheet" href="/resources/space.css" />
 
+          <link href='/resources/highlighter/styles/shCoreCXF.css' 
rel='stylesheet' type='text/css' />
+    <link href='/resources/highlighter/styles/shThemeCXF.css' rel='stylesheet' 
type='text/css' />
+    <script src='/resources/highlighter/scripts/shCore.js' 
type='text/javascript'></script>
+          <script src='/resources/highlighter/scripts/shBrushJava.js' 
type='text/javascript'></script>
+          <script src='/resources/highlighter/scripts/shBrushXml.js' 
type='text/javascript'></script>
+        <script>
+      SyntaxHighlighter.defaults['toolbar'] = false;
+      SyntaxHighlighter.all();
+    </script>
   
   <link href="/styles/style.css" rel="stylesheet" type="text/css"/>
 
@@ -67,7 +76,41 @@
       </div>
 
       <div id="content">
-                <div id="ConfluenceContent"><p>&#160;</p><parameter 
ac:name="hidden">true</parameter><parameter 
ac:name="atlassian-macro-output-type">BLOCK</parameter><rich-text-body><p>How 
to create a library of your custom 
components</p></rich-text-body><rich-text-body><p>This page has not yet been 
fully updated for Tapestry 5.4. Things are different and simpler in 5.4 than in 
previous releases.</p></rich-text-body><h1 
id="ComponentLibraries-CreatingComponentLibraries">Creating Component 
Libraries</h1><p>Nearly every Tapestry application includes a least a couple of 
custom components, specific to the application. What's exciting about Tapestry 
is how easy it is to package components for reuse across many applications ... 
and the fact that applications using a component library need no special 
configuration.</p><parameter ac:name="style">float:right</parameter><parameter 
ac:name="title">Related Articles</parameter><parameter 
ac:name="class">aui-label</parameter><rich-text-body><par
 ameter ac:name="showLabels">false</parameter><parameter 
ac:name="showSpace">false</parameter><parameter ac:name="title">Related 
Articles</parameter><parameter ac:name="cql">label = "components" and space = 
currentSpace()</parameter></rich-text-body><p>A Tapestry component library 
consists of components (and optionally mixins, pages and component base 
classes). In addition, a component library will have a module that can define 
new services (needed by the components) or configure other services present in 
Tapestry. Finally, components can be packaged with <em>assets</em>: resources 
such as images, stylesheets and JavaScript libraries that need to be provided 
to the client web browser.</p><p>We're going to create a somewhat insipid 
component that displays a large happy face icon.</p><p>Tapestry doesn't mandate 
that you use any build system, but we'll assume for the moment that you are 
using Maven 2. In that case, you'll have a pom.xml file something like the 
following:</p><parameter a
 c:name="language">xml</parameter><parameter 
ac:name="title">pom.xml</parameter><plain-text-body>&lt;project&gt;
+                <div id="ConfluenceContent"><p>&#160;</p><div 
class="confluence-information-macro confluence-information-macro-warning"><span 
class="aui-icon aui-icon-small aui-iconfont-error 
confluence-information-macro-icon"></span><div 
class="confluence-information-macro-body"><p>This page has not yet been fully 
updated for Tapestry 5.4. Things are different and simpler in 5.4 than in 
previous releases.</p></div></div><h1 
id="ComponentLibraries-CreatingComponentLibraries">Creating Component 
Libraries</h1><p>Nearly every Tapestry application includes a least a couple of 
custom components, specific to the application. What's exciting about Tapestry 
is how easy it is to package components for reuse across many applications ... 
and the fact that applications using a component library need no special 
configuration.</p><div class="aui-label" style="float:right" title="Related 
Articles">
+
+
+
+
+
+
+
+
+<h3>Related Articles</h3>
+
+<ul class="content-by-label"><li>
+        <div>
+                <span class="icon aui-icon aui-icon-small 
aui-iconfont-page-default" title="Page">Page:</span>        </div>
+
+        <div class="details">
+                        <a  href="component-reference.html">Component 
Reference</a>
+                
+                        
+                    </div>
+    </li><li>
+        <div>
+                <span class="icon aui-icon aui-icon-small 
aui-iconfont-page-default" title="Page">Page:</span>        </div>
+
+        <div class="details">
+                        <a  href="component-libraries.html">Component 
Libraries</a>
+                
+                        
+                    </div>
+    </li></ul>
+</div>
+
+
+<p>A Tapestry component library consists of components (and optionally mixins, 
pages and component base classes). In addition, a component library will have a 
module that can define new services (needed by the components) or configure 
other services present in Tapestry. Finally, components can be packaged with 
<em>assets</em>: resources such as images, stylesheets and JavaScript libraries 
that need to be provided to the client web browser.</p><p>We're going to create 
a somewhat insipid component that displays a large happy face 
icon.</p><p>Tapestry doesn't mandate that you use any build system, but we'll 
assume for the moment that you are using Maven 2. In that case, you'll have a 
pom.xml file something like the following:</p><div class="code panel pdl" 
style="border-width: 1px;"><div class="codeHeader panelHeader pdl" 
style="border-bottom-width: 1px;"><b>pom.xml</b></div><div class="codeContent 
panelContent pdl">
+<pre class="brush: xml; gutter: false; theme: Default" 
style="font-size:12px;">&lt;project&gt;
   &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
   &lt;groupId&gt;org.example&lt;/groupId&gt;
   &lt;artifactId&gt;happylib&lt;/artifactId&gt;
@@ -134,7 +177,9 @@
     
&lt;tapestry-release-version&gt;5.4-beta-28&lt;/tapestry-release-version&gt;
   &lt;/properties&gt;
 &lt;/project&gt;
-</plain-text-body><p>You will need to modify the Tapestry release version 
number ("5.2.0" in the listing above) to reflect the current version of 
Tapestry when you create your component library.</p><p>We'll go into more 
detail about the relevant portions of this POM in the later sections.</p><h2 
id="ComponentLibraries-Step1:Chooseabasepackagename">Step 1: Choose a base 
package name</h2><p>Just as with Tapestry applications, Tapestry component 
libraries should have a <em>unique</em> base package name. In this example, 
we'll use <code>org.examples.happylib</code>.</p><p>As with an application, 
we'll follow the conventions: we'll place the module for this library inside 
the services package, and place pages and components under their respective 
packages.</p><h2 
id="ComponentLibraries-Step2:Createyourpagesand/orcomponents">Step 2: Create 
your pages and/or components</h2><p>Our component is very simple:</p><parameter 
ac:name="language">java</parameter><parameter ac:name="title">HappyIcon
 .java</parameter><plain-text-body>package org.example.happylib.components;
+</pre>
+</div></div><p>You will need to modify the Tapestry release version number 
("5.2.0" in the listing above) to reflect the current version of Tapestry when 
you create your component library.</p><p>We'll go into more detail about the 
relevant portions of this POM in the later sections.</p><h2 
id="ComponentLibraries-Step1:Chooseabasepackagename">Step 1: Choose a base 
package name</h2><p>Just as with Tapestry applications, Tapestry component 
libraries should have a <em>unique</em> base package name. In this example, 
we'll use <code>org.examples.happylib</code>.</p><p>As with an application, 
we'll follow the conventions: we'll place the module for this library inside 
the services package, and place pages and components under their respective 
packages.</p><h2 
id="ComponentLibraries-Step2:Createyourpagesand/orcomponents">Step 2: Create 
your pages and/or components</h2><p>Our component is very simple:</p><div 
class="code panel pdl" style="border-width: 1px;"><div class="codeHeader 
panelHeade
 r pdl" style="border-bottom-width: 1px;"><b>HappyIcon.java</b></div><div 
class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">package org.example.happylib.components;
 
 import org.apache.tapestry5.Asset;
 import org.apache.tapestry5.MarkupWriter;
@@ -155,7 +200,9 @@ public class HappyIcon
         return false;
     }
 }
-</plain-text-body><p>HappyIcon appears inside the components sub-package. The 
happyIcon field is injected with the the Asset for the file 
<code>happy.jpg</code>. The path specified with the @Path annotation is 
relative to the <code>HappyIcon.class</code> file; it should be stored in the 
project under 
<code>src/main/resources/org/example/happylib/components</code>.</p><p>Tapestry 
ensures that the <code>happy.jpg</code> asset can be accessed from the client 
web browser; the src attribute of the &lt;img&gt; tag will be a URL that 
directly accesses the image file ... there's no need to unpackage the 
<code>happy.jpg</code> file. This works for any asset file stored under the 
library's root package.</p><p>This component renders out an 
<code>&lt;img&gt;</code> tag for the icon.</p><p>Often, a component library 
will have many different components, or even pages.</p><h2 
id="ComponentLibraries-Step3:Chooseavirtualfoldername">Step 3: Choose a virtual 
folder name</h2><p>In Tapestry, components 
 that have been packaged in a library are referenced using a virtual folder 
name. It's effectively as if the application had a new root-level folder 
containing the components.</p><p>In our example, we'll use "happy" as the 
folder name. That means the application can include the HappyIcon component in 
the template using any of the following, which are all 
equivalent:</p><ul><li>&lt;t:happy.happyicon/&gt;</li><li>&lt;t:happy.icon/&gt;</li><li>&lt;img
 t:type="happy.happyicon"/&gt;</li><li>&lt;img 
t:type="happy/icon"/&gt;</li></ul><p>Why "icon" vs. "happyicon"? Tapestry 
notices that the folder name, "happy" is a prefix or suffix of the class name 
("HappyIcon") and creates an alias that strips off the prefix (or suffix). To 
Tapestry, they are completely identical: two different aliases for the same 
component class name.</p><p>The above naming is somewhat clumsy, and can be 
improved by introducing an additional namespace into the 
template:</p><parameter ac:name="language">xml</parameter><p
 lain-text-body>&lt;html 
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd";
+</pre>
+</div></div><p>HappyIcon appears inside the components sub-package. The 
happyIcon field is injected with the the Asset for the file 
<code>happy.jpg</code>. The path specified with the @Path annotation is 
relative to the <code>HappyIcon.class</code> file; it should be stored in the 
project under 
<code>src/main/resources/org/example/happylib/components</code>.</p><p>Tapestry 
ensures that the <code>happy.jpg</code> asset can be accessed from the client 
web browser; the src attribute of the &lt;img&gt; tag will be a URL that 
directly accesses the image file ... there's no need to unpackage the 
<code>happy.jpg</code> file. This works for any asset file stored under the 
library's root package.</p><p>This component renders out an 
<code>&lt;img&gt;</code> tag for the icon.</p><p>Often, a component library 
will have many different components, or even pages.</p><h2 
id="ComponentLibraries-Step3:Chooseavirtualfoldername">Step 3: Choose a virtual 
folder name</h2><p>In Tapestry, components that h
 ave been packaged in a library are referenced using a virtual folder name. 
It's effectively as if the application had a new root-level folder containing 
the components.</p><p>In our example, we'll use "happy" as the folder name. 
That means the application can include the HappyIcon component in the template 
using any of the following, which are all 
equivalent:</p><ul><li>&lt;t:happy.happyicon/&gt;</li><li>&lt;t:happy.icon/&gt;</li><li>&lt;img
 t:type="happy.happyicon"/&gt;</li><li>&lt;img 
t:type="happy/icon"/&gt;</li></ul><p>Why "icon" vs. "happyicon"? Tapestry 
notices that the folder name, "happy" is a prefix or suffix of the class name 
("HappyIcon") and creates an alias that strips off the prefix (or suffix). To 
Tapestry, they are completely identical: two different aliases for the same 
component class name.</p><p>The above naming is somewhat clumsy, and can be 
improved by introducing an additional namespace into the template:</p><div 
class="code panel pdl" style="border-width: 1px;
 "><div class="codeContent panelContent pdl">
+<pre class="brush: xml; gutter: false; theme: Default" 
style="font-size:12px;">&lt;html 
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd";
   xmlns:h="tapestry-library:happy"&gt;
 
   ...
@@ -164,7 +211,9 @@ public class HappyIcon
 
   ...
 &lt;/html&gt;
-</plain-text-body><p>The special namespace mapping for sets up namespace 
prefix "h:" to mean the same as "happy/". It then becomes possible to reference 
components within the happy virtual folder directly.</p><h2 
id="ComponentLibraries-Step4:Configurethevirtualfolder">Step 4: Configure the 
virtual folder</h2><p>Tapestry needs to know where to search for your component 
class. This is accomplished in your library's IoC module class, by making a 
<em>contribution</em> to the ComponentClassResolver service 
configuration.</p><p>At application startup, Tapestry will read the library 
module along with all other modules and configure the ComponentClassResolver 
service using information in the module:</p><parameter 
ac:name="language">java</parameter><parameter 
ac:name="title">HappyModule.java</parameter><plain-text-body>package 
org.example.happylib.services;
+</pre>
+</div></div><p>The special namespace mapping for sets up namespace prefix "h:" 
to mean the same as "happy/". It then becomes possible to reference components 
within the happy virtual folder directly.</p><h2 
id="ComponentLibraries-Step4:Configurethevirtualfolder">Step 4: Configure the 
virtual folder</h2><p>Tapestry needs to know where to search for your component 
class. This is accomplished in your library's IoC module class, by making a 
<em>contribution</em> to the ComponentClassResolver service 
configuration.</p><p>At application startup, Tapestry will read the library 
module along with all other modules and configure the ComponentClassResolver 
service using information in the module:</p><div class="code panel pdl" 
style="border-width: 1px;"><div class="codeHeader panelHeader pdl" 
style="border-bottom-width: 1px;"><b>HappyModule.java</b></div><div 
class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">package org.example.happylib.services;
 
 import org.apache.tapestry5.ioc.Configuration;
 import org.apache.tapestry5.services.LibraryMapping;
@@ -176,7 +225,9 @@ public class HappyModule
         configuration.add(new LibraryMapping("happy", "org.example.happylib"));
     }
 }
-</plain-text-body><p>The ComponentClassResolver service is responsible for 
mapping libraries to packages; it takes as a contribution a collection of these 
LibraryMapping objects. Every module may make its own contribution to the 
ComponentClassResolver service, mapping its own package 
("org.example.happylib") to its own folder ("happy").</p><p>This module class 
is also where you would define new services that can be accessed by your 
components (or other parts of the application).</p><rich-text-body><p>It is 
possible to add a mapping for "core", the core library for Tapestry components; 
all the built-in Tapestry components (TextField, BeanEditForm, Grid, etc.) are 
actually in the core library. When Tapestry doesn't find a component in your 
application, it next searches inside the "core" library. Contributing an 
additional package as "core" simply extends the number of packages searched for 
core components (it doesn't replace Tapestry's default package, 
org.apache.tapestry5.corelib). A
 dding to "core" is sometimes reasonable, if you ensure that there is virtually 
no chance of a naming conflict (via different modules contributing packages to 
core with conflicting class names).</p></rich-text-body><h2 
id="ComponentLibraries-Step5:Configurethemoduletoautoload">Step 5: Configure 
the module to autoload</h2><p>For Tapestry to load your module at application 
startup, it is necessary to put an entry in the JAR manifest. This is taken 
care of in the pom.xml above:</p><parameter 
ac:name="language">xml</parameter><parameter ac:name="title">pom.xml 
(partial)</parameter><plain-text-body>      &lt;plugin&gt;
+</pre>
+</div></div><p>The ComponentClassResolver service is responsible for mapping 
libraries to packages; it takes as a contribution a collection of these 
LibraryMapping objects. Every module may make its own contribution to the 
ComponentClassResolver service, mapping its own package 
("org.example.happylib") to its own folder ("happy").</p><p>This module class 
is also where you would define new services that can be accessed by your 
components (or other parts of the application).</p><div 
class="confluence-information-macro confluence-information-macro-note"><span 
class="aui-icon aui-icon-small aui-iconfont-warning 
confluence-information-macro-icon"></span><div 
class="confluence-information-macro-body"><p>It is possible to add a mapping 
for "core", the core library for Tapestry components; all the built-in Tapestry 
components (TextField, BeanEditForm, Grid, etc.) are actually in the core 
library. When Tapestry doesn't find a component in your application, it next 
searches inside the "core" 
 library. Contributing an additional package as "core" simply extends the 
number of packages searched for core components (it doesn't replace Tapestry's 
default package, org.apache.tapestry5.corelib). Adding to "core" is sometimes 
reasonable, if you ensure that there is virtually no chance of a naming 
conflict (via different modules contributing packages to core with conflicting 
class names).</p></div></div><h2 
id="ComponentLibraries-Step5:Configurethemoduletoautoload">Step 5: Configure 
the module to autoload</h2><p>For Tapestry to load your module at application 
startup, it is necessary to put an entry in the JAR manifest. This is taken 
care of in the pom.xml above:</p><div class="code panel pdl" 
style="border-width: 1px;"><div class="codeHeader panelHeader pdl" 
style="border-bottom-width: 1px;"><b>pom.xml (partial)</b></div><div 
class="codeContent panelContent pdl">
+<pre class="brush: xml; gutter: false; theme: Default" 
style="font-size:12px;">      &lt;plugin&gt;
            &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
            &lt;artifactId&gt;maven-jar-plugin&lt;/artifactId&gt;
            &lt;configuration&gt;
@@ -187,15 +238,20 @@ public class HappyModule
            &lt;/archive&gt;
            &lt;/configuration&gt;
        &lt;/plugin&gt;
-</plain-text-body><h2 id="ComponentLibraries-Step6:ExtendingClientAccess">Step 
6: Extending Client Access</h2><p>As of Tapestry 5.2, a new step is needed: 
extending access for the assets. This is accomplished in your library's module 
class, HappyModule:</p><parameter 
ac:name="language">java</parameter><plain-text-body>public static void 
contributeRegexAuthorizer(Configuration&lt;String&gt; configuration)
+</pre>
+</div></div><h2 id="ComponentLibraries-Step6:ExtendingClientAccess">Step 6: 
Extending Client Access</h2><p>As of Tapestry 5.2, a new step is needed: 
extending access for the assets. This is accomplished in your library's module 
class, HappyModule:</p><div class="code panel pdl" style="border-width: 
1px;"><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">public static void 
contributeRegexAuthorizer(Configuration&lt;String&gt; configuration)
 {
     configuration.add("^org/example/happylib/.*\\.jpg$");
 }
-</plain-text-body><p>This contribution uses a regular expression to identify 
that any resource on the classpath under the org/example/happylib folder with a 
jpg extension is allowed. If you had a mix of different image types, you could 
replace jpg with (jpg|gif|png).</p><h2 
id="ComponentLibraries-Step7:VersioningAssets">Step 7: Versioning 
Assets</h2><p>Classpath assets, those packaged in JAR files (such as the 
happy.jpg asset) are retrieved by the client web browser using a URL that 
reflects the package name. Tapestry users a special virtual folder, /assets, 
under the context folder for this purpose.</p><p>The image file here is exposed 
to the web browser via the URL 
/happyapp/assets/org/example/happylib/components/happy.jpg (this assumes that 
the application was deployed as happyapp.war).</p><p>Tapestry uses a far-future 
expiration date for classpath assets; this allows browsers to aggressively 
cache the file, but in Tapestry 5.1 and earlier this causes a problem when a 
later versi
 on of the library changes the file. This is discussed in detail in <a  
class="external-link" 
href="http://developer.yahoo.com/performance/rules.html#expires"; 
rel="nofollow">Yahoo's Performance Best Practices</a>.</p><p>To handle this 
problem in Tapestry 5.1 and earlier, you should map your library assets to a 
versioned folder. This can be accomplished using another contribution from the 
HappyModule, this time to the ClasspathAssetAliasManager service whose 
configuration maps a virtual folder underneath /assets to a 
package:</p><parameter 
ac:name="language">java</parameter><plain-text-body>public static void 
contributeClasspathAssetAliasManager(MappedConfiguration&lt;String, String&gt; 
configuration)
+</pre>
+</div></div><p>This contribution uses a regular expression to identify that 
any resource on the classpath under the org/example/happylib folder with a jpg 
extension is allowed. If you had a mix of different image types, you could 
replace jpg with (jpg|gif|png).</p><h2 
id="ComponentLibraries-Step7:VersioningAssets">Step 7: Versioning 
Assets</h2><p>Classpath assets, those packaged in JAR files (such as the 
happy.jpg asset) are retrieved by the client web browser using a URL that 
reflects the package name. Tapestry users a special virtual folder, /assets, 
under the context folder for this purpose.</p><p>The image file here is exposed 
to the web browser via the URL 
/happyapp/assets/org/example/happylib/components/happy.jpg (this assumes that 
the application was deployed as happyapp.war).</p><p>Tapestry uses a far-future 
expiration date for classpath assets; this allows browsers to aggressively 
cache the file, but in Tapestry 5.1 and earlier this causes a problem when a 
later version of 
 the library changes the file. This is discussed in detail in <a  
class="external-link" 
href="http://developer.yahoo.com/performance/rules.html#expires"; 
rel="nofollow">Yahoo's Performance Best Practices</a>.</p><p>To handle this 
problem in Tapestry 5.1 and earlier, you should map your library assets to a 
versioned folder. This can be accomplished using another contribution from the 
HappyModule, this time to the ClasspathAssetAliasManager service whose 
configuration maps a virtual folder underneath /assets to a package:</p><div 
class="code panel pdl" style="border-width: 1px;"><div class="codeContent 
panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">public static void 
contributeClasspathAssetAliasManager(MappedConfiguration&lt;String, String&gt; 
configuration)
 {
     configuration.add("happylib/1.0", "org/example/happylib");
 }
-</plain-text-body><p>With this in place, and the library and applications 
rebuilt and redeployed, the URL for happy.jpg becomes 
/happyapp/assets/happylib/1.0/components/happy.jpg. This is shorter, but also 
incorporates a version number ("1.0") that can be changed in a later 
release.</p>
+</pre>
+</div></div><p>With this in place, and the library and applications rebuilt 
and redeployed, the URL for happy.jpg becomes 
/happyapp/assets/happylib/1.0/components/happy.jpg. This is shorter, but also 
incorporates a version number ("1.0") that can be changed in a later 
release.</p>
 
 <div class="confluence-information-macro 
confluence-information-macro-information"><p class="title">Added in 
5.2</p><span class="aui-icon aui-icon-small aui-iconfont-info 
confluence-information-macro-icon"></span><div 
class="confluence-information-macro-body">
 </div></div>


Reply via email to