tapestry-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Tapestry > Frequently Asked Questions
Date Tue, 17 Aug 2010 17:19:00 GMT
<html>
<head>
    <base href="https://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/1810/9/8/_/styles/combined.css?spaceKey=TAPESTRY&amp;forWysiwyg=true" type="text/css">
    </head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
    <h2><a href="https://cwiki.apache.org/confluence/display/TAPESTRY/Frequently+Asked+Questions">Frequently Asked Questions</a></h2>
    <h4>Page <b>edited</b> by             <a href="https://cwiki.apache.org/confluence/display/~hlship">Howard M. Lewis Ship</a>
    </h4>
        <br/>
                         <h4>Changes (2)</h4>
                                 
    
<div id="page-diffs">
            <table class="diff" cellpadding="0" cellspacing="0">
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">{children:all=true|excerpt=true} <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h1. Contents <br> <br>{toc} <br> <br>h1. Frequently Asked Questions <br> <br>{include:Ajax Components} <br>{include:General Questions} <br>{include:Integration with existing applications} <br>{include:Limitations} <br>{include:Page And Component Classes} <br>{include:Specific Errors} <br>{include:Tapestry Inversion of Control Container} <br>{include:Templating and Markup} <br></td></tr>
        </table>
</div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <h1><a name="FrequentlyAskedQuestions-Contents"></a>Contents</h1>

<div>
<ul>
    <li><a href='#FrequentlyAskedQuestions-Contents'>Contents</a></li>
    <li><a href='#FrequentlyAskedQuestions-FrequentlyAskedQuestions'>Frequently Asked Questions</a></li>
    <li><a href='#FrequentlyAskedQuestions-DoIhavetospecifyboth%7B%7Bid%7D%7Dand%7B%7Bt%3Aid%7D%7DforZonecomponents%3F'>Do I have to specify both <tt>id</tt> and <tt>t:id</tt> for Zone components?</a></li>
    <li><a href='#FrequentlyAskedQuestions-HowdoIgetstartedwithTapestry%3F'>How do I get started with Tapestry?</a></li>
    <li><a href='#FrequentlyAskedQuestions-WhydoesTapestryusePrototype%3FWhynotinsertfavoriteJavaScriptlibraryhere%3F'>Why does Tapestry use Prototype? Why not <em>insert favorite JavaScript library here</em>?</a></li>
    <li><a href='#FrequentlyAskedQuestions-WhydoesTapestryhaveitsownInversionofControlContainer%3FWhynotSpringorGuice%3F'>Why does Tapestry have its own Inversion of Control Container? Why not Spring or Guice?</a></li>
    <li><a href='#FrequentlyAskedQuestions-HowdoImakeaformonaJSPsubmitintoTapestry%3F'>How do I make a form on a JSP submit into Tapestry?</a></li>
    <li><a href='#FrequentlyAskedQuestions-HowdoIshareinformationbetweenaJSPapplicationandtheTapestryapplication%3F'>How do I share information between a JSP application and the Tapestry application?</a></li>
    <li><a href='#FrequentlyAskedQuestions-HowdoIaddnewcomponentstoanexistingpagedynamically%3F'>How do I add new components to an existing page dynamically?</a></li>
    <li><a href='#FrequentlyAskedQuestions-Whydoesn%27tmyserviceimplementationreloadwhenIchangeit%3F'>Why doesn't my service implementation reload when I change it?</a></li>
    <li><a href='#FrequentlyAskedQuestions-PageAndComponentClasses'>Page And Component Classes</a></li>
<ul>
    <li><a href='#FrequentlyAskedQuestions-What%27sthedifferencebetweenapageandacomponent%3F'>What's the difference between a page and a component?</a></li>
</ul>
    <li><a href='#FrequentlyAskedQuestions-HowdoIstoremypageclassesinadifferentpackage%3F'>How do I store my page classes in a different package?</a></li>
    <li><a href='#FrequentlyAskedQuestions-Whydomyinstancevariableshavetobeprivate%3F'>Why do my instance variables have to be private?</a></li>
<ul>
    <li><a href='#FrequentlyAskedQuestions-Whydon%27tmyinformalparametersshowup%3F'>Why don't my informal parameters show up?</a></li>
    <li><a href='#FrequentlyAskedQuestions-WhydoIgetjava.lang.LinkageErrorwhenIinvokepublicmethodsofmypageclasses%3F'>Why do I get java.lang.LinkageError when I invoke public methods of my page classes?</a></li>
</ul>
    <li><a href='#FrequentlyAskedQuestions-WhydoIgettheexception%22Noserviceimplementstheinterfaceorg.apache.tapestry5.internal.InternalComponentResources%22whentryingtousetheBeanEditFormcomponent%3F'>Why do I get the exception "No service implements the interface org.apache.tapestry5.internal.InternalComponentResources" when trying to use the BeanEditForm component?</a></li>
    <li><a href='#FrequentlyAskedQuestions-WhydoIneedtodefineaninterfaceformyservices%3FWhycan%27tIjustusetheclassitself%3F'>Why do I need to define an interface for my services? Why can't I just use the class itself?</a></li>
    <li><a href='#FrequentlyAskedQuestions-WhydoIgetaSAXParseExceptionwhenIuseanHTMLentity%2Csuchas%7B%7B%26amp%3Bnbsp%3B%7D%7Dinmytemplate%3F'>Why do I get a SAXParseException when I use an HTML entity, such as <tt>&amp;nbsp;</tt> in my template?</a></li>
    <li><a href='#FrequentlyAskedQuestions-Whydosomeimagesinmypageshowupasbrokenlinks%3F'>Why do some images in my page show up as broken links?</a></li>
    <li><a href='#FrequentlyAskedQuestions-What%27sthedifferencebetween%7B%7Bid%7D%7Dand%7B%7Bt%3Aid%7D%7D%3F'>What's the difference between <tt>id</tt> and <tt>t:id</tt>?</a></li>
</ul></div>

<h1><a name="FrequentlyAskedQuestions-FrequentlyAskedQuestions"></a>Frequently Asked Questions</h1>

<h1><a name="FrequentlyAskedQuestions-DoIhavetospecifyboth%7B%7Bid%7D%7Dand%7B%7Bt%3Aid%7D%7DforZonecomponents%3F"></a>Do I have to specify both <tt>id</tt> and <tt>t:id</tt> for Zone components?</h1>

<p>The examples for the Zone component (in the Component Reference) consistently specify both <tt>id</tt> and <tt>t:id</tt> and this is probably a good idea.</p>

<p>Generally speaking, if you don't specify the client-side id (the <tt>id</tt> attribute), it will be the same as the Tapestry component id (<tt>t:id</tt>).</p>

<p>However, there are any number of exceptions to this rule. The Zone may be rendering inside a Loop (in which case, each rendering will have a unique client side id). The Zone may be rendering as part of a partial page render, in which case, a random unique id is inserted into the id. There are other examples where Tapestry component ids in nested components may also clash.</p>

<p>The point is, to be sure, specify the exact client id.  This will be the value for the <tt>zone</tt> parameter of the triggering component (such as a Form, PageLink, ActionLink, etc.).</p>
<h1><a name="FrequentlyAskedQuestions-HowdoIgetstartedwithTapestry%3F"></a>How do I get started with Tapestry?</h1>

<p>The easiest way to get started is to use <a href="http://maven.apache.org" class="external-link" rel="nofollow">Apache Maven</a> to create your initial project; Maven can use an <em>archetype</em> (a kind of project template) to create a bare-bones Tapestry application for you.</p>

<p>One you have Maven installed, execute the command <tt>mvn archetype:generate &#45;DarchetypeCatalog=</tt><tt><a href="http://tapestry.apache.org" class="external-link" rel="nofollow">http://tapestry.apache.org</a></tt>.  Maven will (after performing a large number of one-time downloads) ask you questions about how to create the new project, including a group id (like a package name) and an artifact id for your new project.  Once it is created, you can load it into any IDE and start coding, or use <tt>mvn jetty:run</tt><style type='text/css'>
.FootnoteMarker, .FootnoteNum a {
  background: transparent url(/confluence/download/resources/com.adaptavist.confluence.footnoteMacros:footnote/gfx/footnote.png) no-repeat top right;
  padding: 1px 2px 0px 1px;
  border-left: 1px solid #8898B8;
  border-bottom: 1px solid #6B7C9B;
  margin: 1px;
  text-decoration: none;
}
.FootnoteNum a {
  margin-top: 2px;
  margin-right: 0px;
}
.FootnoteNum {
  font-size: x-small;
  text-align: right;
  padding-bottom: 4px;
}
.footnote-th1 {
  text-align: right;
}
.Footnote {
  padding-left: 7px;
  margin-bottom: 4px;
  border: 1px none #DDDDDD;
  writingMode: tb-rl;
}
.accessibility {
     display: none;
     visibility: hidden;
}
@media aural,braille,embossed {
        .FootnoteMarker, .FootnoteNum a {
         border: 1px solid #000000;
         background: #ffffff none;
    }
    .accessibility {
         display: run-in;
         visibility: visible;
    }
}
</style>
<script type='text/javascript' language='JavaScript'>
//<!--\n
var effectInProgress = {};
var despamEffect = function (id,effectType,duration) {
  if ((effectInProgress[id]) || (typeof(Effect)=="undefined") || (typeof(Effect[effectType])=="undefined")) return;
  new Effect[effectType](id);
  effectInProgress[id]=true;
  setTimeout('effectInProgress[\"'+id+'\"]=false;',duration*1000);
};
var oldFootnoteId = '';
var footnoteHighlight = function(id,pulsateNum) {
  if (oldFootnoteId!='') document.getElementById('Footnote'+oldFootnoteId).style['borderStyle'] = 'none';
  oldFootnoteId = id;
  document.getElementById('Footnote'+id).style['borderStyle'] = 'solid';
  despamEffect('Footnote'+id,'Highlight',1)
  if (pulsateNum) despamEffect('FootnoteNum'+id,'Pulsate',3)
}
var footnoteMarkerHighlight = function(id) {
  if (oldFootnoteId!='') document.getElementById('Footnote'+oldFootnoteId).style['borderStyle'] = 'none';
  oldFootnoteId = '';
  despamEffect('FootnoteMarker'+id,'Pulsate',3)
}
//-->
</script>

<sup id='FootnoteMarker1'>
    <a name='FootnoteMarker1'
        href='#Footnote1'
        onClick='footnoteHighlight("1",true);'
        alt='Footnote: Click here to display the footnote'
        title='Footnote: Click here to display the footnote'
        class='FootnoteMarker'>
            1
    </a>
</sup>
.  Again, more one-time downloads, but then you can open your browser to <a href="http://localhost:8080" class="external-link" rel="nofollow">http://localhost:8080</a> to run the application.</p>

<h1><a name="FrequentlyAskedQuestions-WhydoesTapestryusePrototype%3FWhynotinsertfavoriteJavaScriptlibraryhere%3F"></a>Why does Tapestry use Prototype?  Why not <em>insert favorite JavaScript library here</em>?</h1>

<p>An important goal for Tapestry is seamless DHTML and Ajax integration. To serve that goal, it was important that the built in components<br/>
be capable of Ajax operations, such as dynamically re-rendering parts of the page. Because of that, it made sense to bundle a well-known JavaScript library as part of Tapestry.</p>

<p>At the time (this would be 2006-ish), Prototype and Scriptaculous were well known and well documented, and jQuery was just getting started.</p>

<p>The intent has always been to make this aspect of Tapestry pluggable. This is work expected in Tapestry 5.3, where a kind of adapter layer will be introduced so that Tapestry can be easily customized to work with any of the major JavaScript libraries.  </p>

<h1><a name="FrequentlyAskedQuestions-WhydoesTapestryhaveitsownInversionofControlContainer%3FWhynotSpringorGuice%3F"></a>Why does Tapestry have its own Inversion of Control Container?  Why not Spring or Guice?</h1>

<p>An Inversion of Control Container is <em>the</em> key piece of Tapestry's infrastructure. It is absolutely necessary to create software as robust, performant and extensible as Tapestry.</p>

<p>Tapestry IoC includes a number of features that distinguish itself from other containers:</p>
<ul>
	<li>Configured in code, not XML</li>
	<li>Built-in extension mechanism for services: configurations and contributions</li>
	<li>Built-in aspect oriented programming model (service decorations and advice)</li>
	<li>Easy modularization</li>
	<li>Best-of-breed exception reporting</li>
</ul>


<p>Because Tapestry is implemented on top of its IoC container, and because the container makes it easy to extend or replace any service inside the container, it is possible to make the small changes to Tapestry needed to customize it to any project's needs.</p>

<p>&#8212;<br/>
<table class='Footnotes' style='width: 100%; border:none;' cellspacing='0' cellpadding='0' summary='This table contains one or more notes for references made elsewhere on the page.'>
  <caption class='accessibility'>Footnotes</caption>
  <thead class='accessibility'>
    <tr class='accessibility'>
      <th class='accessibility' id='footnote-th1'>Reference</th>
      <th class='accessibility' id='footnote-th2'>Notes</th>
    </tr>
  </thead>
  <tbody>
    <tr name='Footnote1'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker1'
          onClick='footnoteMarkerHighlight("1");'
          onMouseOver='footnoteHighlight("1",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum1'>
            1
        </a>
      </td>
      <td id='Footnote1'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          Jetty is a well-known high-performance servlet container. Jetty starts up quickly, and implements the official Servlet specification very closely.
      </td>
    </tr>
  </tbody>
</table></p>
<p>You may have an existing JSP (or Struts, Spring MVC, etc.) application that you want to migrate to Tapestry. It's quite common to do this in stages, moving some functionality into Tapestry and leaving other parts, initially, in the other system.</p>

<h1><a name="FrequentlyAskedQuestions-HowdoImakeaformonaJSPsubmitintoTapestry%3F"></a>How do I make a form on a JSP submit into Tapestry?</h1>

<p>Tapestry's Form component does a lot of work while an HTML form is rendering to store all the information needed to handle the form submission in a later request; this is all very specific to Tapestry and the particular construction of your pages and forms; it can't be reproduced from a JSP.</p>

<p>Fortunately, that isn't necessary: you can have a standard HTML Form submit to a Tapestry page, you just don't get to use all of Tapestry's built in conversion and validation logic.</p>

<p>All you need to know is how Tapestry converts page class names to page names (that appear in the URL).  It's basically a matter of stripping off the <em>root-package</em>.<tt>pages</tt> prefix from the fully qualified class name. So, for example, if you are building a login screen as a JSP, you might want to have a Tapestry page to receive the user name and password.  Let's assume the Tapestry page class is <tt>com.example.myapp.pages.LoginForm</tt>; the page name will be <tt>loginform</tt><style type='text/css'>
.FootnoteMarker, .FootnoteNum a {
  background: transparent url(/confluence/download/resources/com.adaptavist.confluence.footnoteMacros:footnote/gfx/footnote.png) no-repeat top right;
  padding: 1px 2px 0px 1px;
  border-left: 1px solid #8898B8;
  border-bottom: 1px solid #6B7C9B;
  margin: 1px;
  text-decoration: none;
}
.FootnoteNum a {
  margin-top: 2px;
  margin-right: 0px;
}
.FootnoteNum {
  font-size: x-small;
  text-align: right;
  padding-bottom: 4px;
}
.footnote-th1 {
  text-align: right;
}
.Footnote {
  padding-left: 7px;
  margin-bottom: 4px;
  border: 1px none #DDDDDD;
  writingMode: tb-rl;
}
.accessibility {
     display: none;
     visibility: hidden;
}
@media aural,braille,embossed {
        .FootnoteMarker, .FootnoteNum a {
         border: 1px solid #000000;
         background: #ffffff none;
    }
    .accessibility {
         display: run-in;
         visibility: visible;
    }
}
</style>
<script type='text/javascript' language='JavaScript'>
//<!--\n
var effectInProgress = {};
var despamEffect = function (id,effectType,duration) {
  if ((effectInProgress[id]) || (typeof(Effect)=="undefined") || (typeof(Effect[effectType])=="undefined")) return;
  new Effect[effectType](id);
  effectInProgress[id]=true;
  setTimeout('effectInProgress[\"'+id+'\"]=false;',duration*1000);
};
var oldFootnoteId = '';
var footnoteHighlight = function(id,pulsateNum) {
  if (oldFootnoteId!='') document.getElementById('Footnote'+oldFootnoteId).style['borderStyle'] = 'none';
  oldFootnoteId = id;
  document.getElementById('Footnote'+id).style['borderStyle'] = 'solid';
  despamEffect('Footnote'+id,'Highlight',1)
  if (pulsateNum) despamEffect('FootnoteNum'+id,'Pulsate',3)
}
var footnoteMarkerHighlight = function(id) {
  if (oldFootnoteId!='') document.getElementById('Footnote'+oldFootnoteId).style['borderStyle'] = 'none';
  oldFootnoteId = '';
  despamEffect('FootnoteMarker'+id,'Pulsate',3)
}
//-->
</script>

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

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
&lt;form method=<span class="code-quote">"post"</span> action=<span class="code-quote">"/loginform"</span>&gt;

  &lt;input type=<span class="code-quote">"text"</span> value=<span class="code-quote">"userName"</span>/&gt;
  &lt;br/&gt;
  &lt;input type=<span class="code-quote">"password"</span> value=<span class="code-quote">"password"</span>/&gt;
  &lt;br/&gt;
  &lt;input type=<span class="code-quote">"submit"</span> value=<span class="code-quote">"Login"</span>/&gt;

&lt;/form&gt;
</pre>
</div></div>

<p>On the Tapestry side, we can expect that the LoginForm page will be activated; this means that its activate event handler will be invoked.  We can leverage this, and Tapestry's RequestParameter annotation:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> class LoginForm
{
  void onActivate(@RequestParameter(<span class="code-quote">"userName"</span>) <span class="code-object">String</span> userName, @RequestParameter(<span class="code-quote">"password"</span>) <span class="code-object">String</span> password)
  {
     <span class="code-comment">// Validate and store credentials, etc.
</span>  }
}
</pre>
</div></div>

<p>The RequestParameter annotation extracts the named query parameter from the request, coerces its type from String to the parameter type (here, also String) and passes it into the method.</p>


<h1><a name="FrequentlyAskedQuestions-HowdoIshareinformationbetweenaJSPapplicationandtheTapestryapplication%3F"></a>How do I share information between a JSP application and the Tapestry application?</h1>

<p>From the servlet container's point of view, there's no difference between a servlet, a JSP, and an entire Tapestry application. They all share the same ServletContext, and (once created), the same HttpSession.</p>

<p>On the Tapestry side, it is very easy to read and write session attributes:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> class ShowSearchResults
{
  @SessionAttribute
  <span class="code-keyword">private</span> SearchResults searchResults;
}
</pre>
</div></div>

<p>Reading the instance variable <tt>searchResults</tt> is instrumented to instead read the corresponding HttpSession attribute named "searchResults".  You can also specify the <tt>value</tt> attribute of the SessionAttribute annotation to override the default attribute name.</p>

<p>Writing to the field writes into the session.</p>

<p>The session is automatically created as needed.</p>

<p>&#8212;</p>

<p><table class='Footnotes' style='width: 100%; border:none;' cellspacing='0' cellpadding='0' summary='This table contains one or more notes for references made elsewhere on the page.'>
  <caption class='accessibility'>Footnotes</caption>
  <thead class='accessibility'>
    <tr class='accessibility'>
      <th class='accessibility' id='footnote-th1'>Reference</th>
      <th class='accessibility' id='footnote-th2'>Notes</th>
    </tr>
  </thead>
  <tbody>
    <tr name='Footnote1'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker1'
          onClick='footnoteMarkerHighlight("1");'
          onMouseOver='footnoteHighlight("1",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum1'>
            1
        </a>
      </td>
      <td id='Footnote1'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          Tapestry is case insensitive, so LoginForm would work just as well.
      </td>
    </tr>
  </tbody>
</table></p>
<h1><a name="FrequentlyAskedQuestions-HowdoIaddnewcomponentstoanexistingpagedynamically%3F"></a>How do I add new components to an existing page dynamically?</h1>

<p>The short answer here is: <b>you don't</b>. The long answer here is <b>you don't have to, to get the behavior you desire</b>.</p>

<p>One of Tapestry basic values is high scalability: this is expressed in a number of ways, reflecting scalability concerns within a single server, and within a cluster of servers.</p>

<p>Although you code Tapestry pages and components as if they were ordinary POJOs<style type='text/css'>
.FootnoteMarker, .FootnoteNum a {
  background: transparent url(/confluence/download/resources/com.adaptavist.confluence.footnoteMacros:footnote/gfx/footnote.png) no-repeat top right;
  padding: 1px 2px 0px 1px;
  border-left: 1px solid #8898B8;
  border-bottom: 1px solid #6B7C9B;
  margin: 1px;
  text-decoration: none;
}
.FootnoteNum a {
  margin-top: 2px;
  margin-right: 0px;
}
.FootnoteNum {
  font-size: x-small;
  text-align: right;
  padding-bottom: 4px;
}
.footnote-th1 {
  text-align: right;
}
.Footnote {
  padding-left: 7px;
  margin-bottom: 4px;
  border: 1px none #DDDDDD;
  writingMode: tb-rl;
}
.accessibility {
     display: none;
     visibility: hidden;
}
@media aural,braille,embossed {
        .FootnoteMarker, .FootnoteNum a {
         border: 1px solid #000000;
         background: #ffffff none;
    }
    .accessibility {
         display: run-in;
         visibility: visible;
    }
}
</style>
<script type='text/javascript' language='JavaScript'>
//<!--\n
var effectInProgress = {};
var despamEffect = function (id,effectType,duration) {
  if ((effectInProgress[id]) || (typeof(Effect)=="undefined") || (typeof(Effect[effectType])=="undefined")) return;
  new Effect[effectType](id);
  effectInProgress[id]=true;
  setTimeout('effectInProgress[\"'+id+'\"]=false;',duration*1000);
};
var oldFootnoteId = '';
var footnoteHighlight = function(id,pulsateNum) {
  if (oldFootnoteId!='') document.getElementById('Footnote'+oldFootnoteId).style['borderStyle'] = 'none';
  oldFootnoteId = id;
  document.getElementById('Footnote'+id).style['borderStyle'] = 'solid';
  despamEffect('Footnote'+id,'Highlight',1)
  if (pulsateNum) despamEffect('FootnoteNum'+id,'Pulsate',3)
}
var footnoteMarkerHighlight = function(id) {
  if (oldFootnoteId!='') document.getElementById('Footnote'+oldFootnoteId).style['borderStyle'] = 'none';
  oldFootnoteId = '';
  despamEffect('FootnoteMarker'+id,'Pulsate',3)
}
//-->
</script>

<sup id='FootnoteMarker1'>
    <a name='FootnoteMarker1'
        href='#Footnote1'
        onClick='footnoteHighlight("1",true);'
        alt='Footnote: Click here to display the footnote'
        title='Footnote: Click here to display the footnote'
        class='FootnoteMarker'>
            1
    </a>
</sup>
, as deployed by Tapestry they are closer to a traditional servlet: a single instance of each page services requests from multiple threads. Behind the scenes, Tapestry transforms you code, rewriting it on the fly.</p>

<p>What this means is that <em>any</em> incoming request must be handled by a <em>single page instance</em>. Therefore, Tapestry enforces the concept of <b>static structure, dynamic behavior</b>.</p>

<p>Tapestry provides quite a number of ways to vary what content is rendered, well beyond simple conditionals and loops. It is possible to "drag in" components from other pages when rendering a page (other FAQs will expand on this concept). The point is, that although a Tapestry page's structure is very rigid, the order in which the components of the page render does not have to be top to bottom.</p>


<h1><a name="FrequentlyAskedQuestions-Whydoesn%27tmyserviceimplementationreloadwhenIchangeit%3F"></a>Why doesn't my service implementation reload when I change it?</h1>

<p>Live service reloading has some limitations; basically, the implementation only reloads if Tapestry instantiated it directly. Consider the following example module:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> <span class="code-keyword">static</span> void bind(ServiceBinder binder)
{
  binder.bind(ArchiveService.class, ArchiveServiceImpl.class);
}

<span class="code-keyword">public</span> <span class="code-keyword">static</span> JobQueue buildJobQueue(MessageService messageService, Map&lt;<span class="code-object">String</span>,Job&gt; configuration)
{
  JobQueueImpl service = <span class="code-keyword">new</span> JobQueueImpl(configuration);

  messageService.addQueueListener(service);
 
  <span class="code-keyword">return</span> service;
}
</pre>
</div></div>

<p>ArchiveService is reloadable, because Tapestry instantiates <tt>ArchiveServiceImpl</tt> itself.  On the other hand, Tapestry invokes <tt>buildJobQueue()</tt> and it is your code inside the method that instantiates <tt>JobQueueImpl</tt>, so the JobQueue service will not be reloadable.</p>

<p>Finally, only classes whose class files are stored directly on the file system, and not packaged inside JARs, are ever reloadable ... generally, only the services of the application being built (and not services from libraries) will be stored on the file system. This reflects the intent of reloading: as an agile development tool, but not something to be used in deployment.</p>


<p>&#8212;<br/>
<table class='Footnotes' style='width: 100%; border:none;' cellspacing='0' cellpadding='0' summary='This table contains one or more notes for references made elsewhere on the page.'>
  <caption class='accessibility'>Footnotes</caption>
  <thead class='accessibility'>
    <tr class='accessibility'>
      <th class='accessibility' id='footnote-th1'>Reference</th>
      <th class='accessibility' id='footnote-th2'>Notes</th>
    </tr>
  </thead>
  <tbody>
    <tr name='Footnote1'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker1'
          onClick='footnoteMarkerHighlight("1");'
          onMouseOver='footnoteHighlight("1",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum1'>
            1
        </a>
      </td>
      <td id='Footnote1'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          Plain Old Java Object. Tapestry does not require you to extend any base classes or implement any special interfaces.
      </td>
    </tr>
  </tbody>
</table></p>
<h1><a name="FrequentlyAskedQuestions-PageAndComponentClasses"></a>Page And Component Classes</h1>

<h2><a name="FrequentlyAskedQuestions-What%27sthedifferencebetweenapageandacomponent%3F"></a>What's the difference between a page and a component?</h2>

<p>There's very little difference between the two. Pages clases must be in the <em>root-package</em>.<tt>pages</tt> package; components must be in the<br/>
<em>root-package</em>.<tt>components</tt>.  Pages may provide event handlers for certain page-specific events (such as activate and passivate).  Components may have parameters.</p>

<p>Other than that, they are more equal than they are different. They may have templates or may render themselves in code (pages usually have a template, components are more likely to render only in code).</p>

<p>The major difference is that Tapestry page templates may be stored in the web context directory, as if they were static files (they can't be accessed from the client however; a specific rule prevents access to files with the <tt>.tml</tt> extension).</p>

<div class='panelMacro'><table class='warningMacro'><colgroup><col width='24'><col></colgroup><tr><td valign='top'><img src="/confluence/images/icons/emoticons/forbidden.gif" width="16" height="16" align="absmiddle" alt="" border="0"></td><td>It is possible that this feature may be removed in a later release. It is preferred that page templates be stored on the classpath, like component templates.</td></tr></table></div>

<h1><a name="FrequentlyAskedQuestions-HowdoIstoremypageclassesinadifferentpackage%3F"></a>How do I store my page classes in a different package?</h1>

<p>Tapestry is very rigid here; you can't. Page classes must go in <em>root-package</em>.<tt>pages</tt>, component classes in <em>root-package</em>.<tt>components</tt>, etc.</p>

<h1><a name="FrequentlyAskedQuestions-Whydomyinstancevariableshavetobeprivate%3F"></a>Why do my instance variables have to be private?</h1>

<p>Tapestry does a large amount of transformation to your simple POJO classes as it loads them into memory. In many cases, it must locate every read or write of an instance variable and change its behavior; for example, reading a field that is a component parameter will<br/>
cause a property of the containing page or component to be read.</p>

<p>Limiting fields to private means that Tapestry can do the necessary processing one class at a time, as needed, at runtime. More complex<br/>
Aspect Orient Programming systems such as AspectJ can perform similar transformations (and much more complex ones), but requires a dedicated build step (or the introduction of a JVM agent).</p>

<h2><a name="FrequentlyAskedQuestions-Whydon%27tmyinformalparametersshowup%3F"></a>Why don't my informal parameters show up?</h2>

<p>Getting informal parameters to work is in two steps.  First, you must make a call to the <tt>ComponentResources.renderInformalParameters()</tt> method, but just as importantly, you must tell Tapestry that you want the component to support<br/>
informal parameters, using the <tt>SupportsInformalParameters</tt> annotation. Here's a hypothetical component that displays an image based on the value of a <tt>Image</tt> object (presumably, a database entity):</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
@SupportsInformalParameters
<span class="code-keyword">public</span> class DBImage
{
  @Parameter(required=<span class="code-keyword">true</span>)
  <span class="code-keyword">private</span> Image image;

  @Inject
  <span class="code-keyword">private</span> ComponentResources resources;

  <span class="code-object">boolean</span> beginRender(MarkupWriter writer)
  {
    writer.element(<span class="code-quote">"img"</span>, <span class="code-quote">"src"</span>, image.toClientURL(), <span class="code-quote">"class"</span>, <span class="code-quote">"db-image"</span>);

    resources.renderInformalParameters(writer);

    writer.end();

    <span class="code-keyword">return</span> <span class="code-keyword">false</span>;
  }
}
</pre>
</div></div>


<h2><a name="FrequentlyAskedQuestions-WhydoIgetjava.lang.LinkageErrorwhenIinvokepublicmethodsofmypageclasses%3F"></a>Why do I get java.lang.LinkageError when I invoke public methods of my page classes?</h2>

<p>In Tapestry, there are always <em>two</em> versions of page (or component) classes.  The first version is the version loaded by standard class loader: the simple POJO version that you wrote.</p>

<p>The second version is much more complicated; it's the transformed version of your code, with lots of extra hooks and changes to allow the class to operate inside Tapestry. This includes implementing<br/>
new interfaces and methods, adding new constructors, and changing access to existing fields and methods.</p>

<p>Although these two classes have the same fully qualified class name, they are distinct classes because they are loaded by different class loaders.</p>


<table width="100%">
    <tr><td align="left"> 
        <table>     
            <caption align="bottom">
                                 
                                            
                        <a href="/confluence/spaces/gliffy/viewlargediagram.action?name=Class+Loaders&ceoid=23335008&key=TAPESTRY&pageId=23335008"
                                                    target=""
                                                >Full Size</a>
                        
                                  
                         |                     
                        <a href="/confluence/plugins/gliffy/showgliffyeditor.action?name=Class+Loaders&ceoid=23335008&key=TAPESTRY&lastPage=/confluence/display/TAPESTRY/Page+And+Component+Classes&pageId=23335008"
                                                    target=""
                                                >Edit Diagram</a>
                        
                                  
                         |                     
                        <a href="/confluence/spaces/gliffy/removediagram.action?name=Class+Loaders&ceoid=23335008&key=TAPESTRY&lastPage=/confluence/display/TAPESTRY/Page+And+Component+Classes&pageId=23335008&input=true"
                                                    target=""
                                                >Remove Diagram</a>
                        
                          </caption>

            <tr><td>
                            <img style="border: none" usemap="#GLIFFY_MAP_23335008_Class_Loaders" src="/confluence/plugins/servlet/gliffyapi/clientdiagramjpeg?cb=-1014744769&pk=pub&name=Class+Loaders&ceoid=23335008&key=TAPESTRY&size=L&version=3" alt="A&#32;Gliffy&#32;Diagram&#32;named&#58;&#32;Class&#32;Loaders" />
                       </td></tr>
        </table> 
</td></tr>
</table>
<map name='GLIFFY_MAP_23335008_Class_Loaders'></map>

<p>In a Tapestry application, most application classes, including your services, are loaded from the middle class loader.  When a page or component is passed as a parameter to a service, a failure occurs<br/>
(how it is reported varies in different JDK releases) because of the class mismatch.</p>

<p>The solution is to define an interface with the methods that the service will invoke on the page or component instance. The service will expect an object implementing the interface (and doesn't care what class loader loaded<br/>
the implementing class).</p>

<p>Just be sure to put the interface class in a non-controlled package, such as you <em>root-package</em> (and not <em>root-package</em>.<tt>pages</tt>).</p>
<h1><a name="FrequentlyAskedQuestions-WhydoIgettheexception%22Noserviceimplementstheinterfaceorg.apache.tapestry5.internal.InternalComponentResources%22whentryingtousetheBeanEditFormcomponent%3F"></a>Why do I get the exception "No service implements the interface org.apache.tapestry5.internal.InternalComponentResources" when trying to use the BeanEditForm component?</h1>

<p>This can occur when you choose the wrong package for your data object, the object edited by the BeanEditForm component. If you place it in the same package as your pages, Tapestry will treat it like a page, and perform a number of transformation on it, including adding a new constructor.</p>

<p>Only component classes should go in the Tapestry-controlled packages (<tt>pages</tt>, <tt>components</tt>, <tt>mixins</tt> and <tt>base</tt> under your application's root package). By convention, simple data objects should go in a <tt>data</tt> package, and Hibernate entities should go in an <tt>entities</tt> package.</p>
<h1><a name="FrequentlyAskedQuestions-WhydoIneedtodefineaninterfaceformyservices%3FWhycan%27tIjustusetheclassitself%3F"></a>Why do I need to define an interface for my services?  Why can't I just use the class itself?</h1>

<p>First of all: you can do exactly this, but you lose some of the functionality that Tapestry's IoC container provides.</p>

<p>The reason for the split is so that Tapestry can provide functionality for your service around the core service implementation.  It does this by creating <em>proxies</em>: Java classes<br/>
that implement the service interface.  The methods of the proxy will ultimately invoke the methods of your service implementation.</p>

<p>One of the primary purposes for proxies is to encapsulate the service's lifecycle: most services are singletons that are created <em>just in time</em>.  Just in time means only as soon<br/>
as you invoke a method.  What's going on is that the lifecycle proxy (the object that gets injected into pages, components or other service implementations) checks on each method invocation<br/>
to see if the actual service exists yet.  If not, it instantiates and configures it (using proper locking to ensure thread safety), then delegates the method invocation to the service.</p>

<p>If you binding a service class (not a service interface and class), then the service is fully instantiated the first time it is injected, rather than at that first method invocation. Further, you<br/>
can't use decorations or method advices on such a service.</p>

<p>The final reason for the service interface / implementation split is to nudge you towards always coding to an interface, which has manifest benefits for code structure, robustness, and testability.</p>
<h1><a name="FrequentlyAskedQuestions-WhydoIgetaSAXParseExceptionwhenIuseanHTMLentity%2Csuchas%7B%7B%26amp%3Bnbsp%3B%7D%7Dinmytemplate%3F"></a>Why do I get a SAXParseException when I use an HTML entity, such as <tt>&amp;nbsp;</tt> in my template?</h1>

<p>Tapestry uses a standard SAX parser to read your templates. This means that your templates must be <em>well formed</em>: open and close tags must balance, attribute values must be quoted, and entities must be declared. The easiest way to accomplish this is to add a DOCTYPE to your the top of your template:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
&lt;!DOCTYPE html PUBLIC <span class="code-quote">"-//W3C//DTD XHTML 1.0 Strict//EN"</span>
   <span class="code-quote">"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"</span>&gt;
</pre>
</div></div>

<p>Part of the DOCTYPE is the declaration of entities such as <tt>&amp;nbsp;</tt>.</p>

<p>Alternately, you can simply use the numeric version: <tt>&amp;#160</tt>;  This is the exact same character and will render identically in the browser.</p>


<h1><a name="FrequentlyAskedQuestions-Whydosomeimagesinmypageshowupasbrokenlinks%3F"></a>Why do some images in my page show up as broken links?</h1>

<p>You have to be careful when using relative URLs inside page templates; the base URL may not always be what you expect.  For example, inside your <tt>ViewUser.tml</tt> file, you may have:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
  <span class="code-tag">&lt;img class=<span class="code-quote">"icon"</span> src=<span class="code-quote">"icons/admin.png"</span>/&gt;</span>${user.name} has Administrative access
</pre>
</div></div>

<p>This makes sense; <tt>ViewUser.tml</tt> is in the web context, as is the <tt>icons</tt> folder.  The default URL for this page will be <tt>/viewuser</tt><style type='text/css'>
.FootnoteMarker, .FootnoteNum a {
  background: transparent url(/confluence/download/resources/com.adaptavist.confluence.footnoteMacros:footnote/gfx/footnote.png) no-repeat top right;
  padding: 1px 2px 0px 1px;
  border-left: 1px solid #8898B8;
  border-bottom: 1px solid #6B7C9B;
  margin: 1px;
  text-decoration: none;
}
.FootnoteNum a {
  margin-top: 2px;
  margin-right: 0px;
}
.FootnoteNum {
  font-size: x-small;
  text-align: right;
  padding-bottom: 4px;
}
.footnote-th1 {
  text-align: right;
}
.Footnote {
  padding-left: 7px;
  margin-bottom: 4px;
  border: 1px none #DDDDDD;
  writingMode: tb-rl;
}
.accessibility {
     display: none;
     visibility: hidden;
}
@media aural,braille,embossed {
        .FootnoteMarker, .FootnoteNum a {
         border: 1px solid #000000;
         background: #ffffff none;
    }
    .accessibility {
         display: run-in;
         visibility: visible;
    }
}
</style>
<script type='text/javascript' language='JavaScript'>
//<!--\n
var effectInProgress = {};
var despamEffect = function (id,effectType,duration) {
  if ((effectInProgress[id]) || (typeof(Effect)=="undefined") || (typeof(Effect[effectType])=="undefined")) return;
  new Effect[effectType](id);
  effectInProgress[id]=true;
  setTimeout('effectInProgress[\"'+id+'\"]=false;',duration*1000);
};
var oldFootnoteId = '';
var footnoteHighlight = function(id,pulsateNum) {
  if (oldFootnoteId!='') document.getElementById('Footnote'+oldFootnoteId).style['borderStyle'] = 'none';
  oldFootnoteId = id;
  document.getElementById('Footnote'+id).style['borderStyle'] = 'solid';
  despamEffect('Footnote'+id,'Highlight',1)
  if (pulsateNum) despamEffect('FootnoteNum'+id,'Pulsate',3)
}
var footnoteMarkerHighlight = function(id) {
  if (oldFootnoteId!='') document.getElementById('Footnote'+oldFootnoteId).style['borderStyle'] = 'none';
  oldFootnoteId = '';
  despamEffect('FootnoteMarker'+id,'Pulsate',3)
}
//-->
</script>

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

<p>However, most likely, the ViewUser page has a page activation context to identify which user is to be displayed:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> class ViewUser

  @Property
  @PageActivationContext
  <span class="code-keyword">private</span> User user;

  . . .
</pre>
</div></div>

<p>With a page activation context, the URL for the page will incorporate the id of the User object, something like <tt>/viewuser/37371</tt>.  This is why the relative URLs to the <tt>admin.png</tt> image is broken: the base path is relative to the page's URL, not to the page template
<sup id='FootnoteMarker2'>
    <a name='FootnoteMarker2'
        href='#Footnote2'
        onClick='footnoteHighlight("2",true);'
        alt='Footnote: Click here to display the footnote'
        title='Footnote: Click here to display the footnote'
        class='FootnoteMarker'>
            2
    </a>
</sup>
.</p>

<p>One solution would be to predict what the page URL will be, and adjust the path for that:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
  &lt;img class=<span class="code-quote">"icon"</span> src=<span class="code-quote">"../icons/admin.png"</span>/&gt;${user.name} has Administrative access
</pre>
</div></div>

<p>But this has its own problems; the page activation context may vary in length at different times, or the template in question may be a component used across many different pages, making it difficult to predict what the correct relative URL would be.</p>

<p>The <em>best</em> solution for this situation, one that will be sure to work in all pages and all components, is to make use of the <tt>context:</tt> binding prefix:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
  &lt;img class=<span class="code-quote">"icon"</span> src=<span class="code-quote">"${context:icons/admin.png}"</span>/&gt;${user.name} has Administrative access
</pre>
</div></div>

<p>The src attribute of the &lt;img&gt; tag will now be bound to a dynamically computed value: the location of the image file relative to the <br/>
web application context. This is especially important for components that may be used on different pages.</p>

<h1><a name="FrequentlyAskedQuestions-What%27sthedifferencebetween%7B%7Bid%7D%7Dand%7B%7Bt%3Aid%7D%7D%3F"></a>What's the difference between <tt>id</tt> and <tt>t:id</tt>?</h1>

<p>You might occasionally see something like the following in a template:</p>

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

<p>Why two ids?  Why are they different?</p>

<p>The <tt>t:id</tt> attribute is the Tapestry component id. This id is unique within its immediate container.  This is the id you might use<br/>
to inject the component into your page class:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
  @InjectComponent
  <span class="code-keyword">private</span> Zone statusZone;
</pre>
</div></div>

<p>The other id is the client id, a unique id for the rendered element within the client-side DOM. JavaScript that needs to access the element uses this id.  For example:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-javascript">
  $('status').hide();
</pre>
</div></div>

<p>In many components, the <tt>id</tt> attribute is an informal parameter; a value from the template that is blindly echoed into the output document.  In other cases, the component itself has an <tt>id</tt> attribute.  Often, in the latter case, the Tapestry component id is the <em>default</em> value for the client id.</p>

<p><table class='Footnotes' style='width: 100%; border:none;' cellspacing='0' cellpadding='0' summary='This table contains one or more notes for references made elsewhere on the page.'>
  <caption class='accessibility'>Footnotes</caption>
  <thead class='accessibility'>
    <tr class='accessibility'>
      <th class='accessibility' id='footnote-th1'>Reference</th>
      <th class='accessibility' id='footnote-th2'>Notes</th>
    </tr>
  </thead>
  <tbody>
    <tr name='Footnote1'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker1'
          onClick='footnoteMarkerHighlight("1");'
          onMouseOver='footnoteHighlight("1",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum1'>
            1
        </a>
      </td>
      <td id='Footnote1'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          Assuming that class ViewUser is in the <em>root-package</em>.<tt>pages</tt> package.
      </td>
    </tr>
    <tr name='Footnote2'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker2'
          onClick='footnoteMarkerHighlight("2");'
          onMouseOver='footnoteHighlight("2",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum2'>
            2
        </a>
      </td>
      <td id='Footnote2'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          In fact, the page template may not even be in the web context, it may be stored on the classpath, as component templates are.
      </td>
    </tr>
  </tbody>
</table></p>
    </div>
        <div id="commentsSection" class="wiki-content pageSection">
        <div style="float: right;">
            <a href="https://cwiki.apache.org/confluence/users/viewnotifications.action" class="grey">Change Notification Preferences</a>
        </div>
        <a href="https://cwiki.apache.org/confluence/display/TAPESTRY/Frequently+Asked+Questions">View Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=23334884&revisedVersion=12&originalVersion=11">View Changes</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message