tapestry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Tapestry > Injection FAQ
Date Tue, 05 Oct 2010 07:50:00 GMT
<html>
<head>
    <base href="https://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/1810/9/12/_/styles/combined.css?spaceKey=TAPESTRY&amp;forWysiwyg=true"
type="text/css">
    </head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
    <h2><a href="https://cwiki.apache.org/confluence/display/TAPESTRY/Injection+FAQ">Injection
FAQ</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 (18)</h4>
                                 
    
<div id="page-diffs">
            <table class="diff" cellpadding="0" cellspacing="0">
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >must not define the type, and any
parameter bindings are merged in: <br> <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code:controls=true|linenumbers=true}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" >  &lt;a t:id=&quot;home&quot;
class=&quot;nav&quot;&gt;Back to home&lt;/a&gt; <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" > <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code:controls=true|linenumbers=true}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" >  @Component(parameters={ &quot;page=index&quot;
}) <br>  private PageLink home; <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" > <br>Here the type of component
is defined by the field type. The field name is matched against the {{t:id}} in the template.
The {{page}} parameter <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >By contrast, {{InjectComponent}} expects
the component to be already defined, and doesn&#39;t allow any configuration of it: <br>
<br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code:controls=true|linenumbers=true}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" >  &lt;t:form t:id=&quot;login&quot;&gt;
.... &lt;/t:form&gt; <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" > <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code:controls=true|linenumbers=true}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" >  @InjectComponent <br>  private
Form login; <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" > <br>Again, we&#39;re matching
the field name to the component id, and you would get an error if the component is not defined
in the template. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >The {{InjectPage}} annotation is used
to inject some page in the application into a field of some other page.  You often see it
used from event handler methods: <br> <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code:controls=true|linenumbers=true}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" >  @InjectPage <br>  private
ConfirmRegistration confirmRegistration; <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >    return confirmRegistration; <br>
 } <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" > <br>This code pattern is used
to configure peristent properties of a page before returning it; Tapestry will send a client
redirect to the page to present <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >type of service to inject: <br>
<br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code:controls=true|linenumbers=true}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" >	@Inject <br>	private ComponentEventResultProcessor
processor; <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" > <br>Which results in the error:
*Service interface org.apache.tapestry5.services.ComponentEventResultProcessor is matched
by 3 services: AjaxComponentEventResultProcessor, ComponentEventResultProcessor, ComponentInstanceResultProcessor.
Automatic dependency resolution requires that exactly one service implement the interface.*
<br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >We need more information than just
the service interface type in order to identify which of the three services to inject. One
possibility is to inject with the correct service id: <br> <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code:controls=true|linenumbers=true}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" >	@InjectService(&quot;ComponentEventResultProcessor&quot;)
<br>	private ComponentEventResultProcessor processor; <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" > <br>This works ... but it is
clumsy. If the service id, &quot;ComponentEventResultProcessor&quot;, ever changes,
this code will break. It&#39;s not _refactoring safe_. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >Instead, we should use marker annotations.
 If we look at {{TapestryModule}}, where the ComponentEventResultProcessor service is defined,
we&#39;ll see it identifies the necessary markers: <br> <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code:controls=true|linenumbers=true}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" >    @Marker( <br>    { Primary.class,
Traditional.class }) <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >        return constructComponentEventResultProcessor(configuration);
<br>    } <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" > <br>When a service has marker
annotations, the annotations present at the _point of injection_ (the field, method parameter,
or constructor parameter) are used to select a matching service.  The list of services that
match by type is then filtered to only include services that have all of the marker annotations
present at the point of injection. <br> <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code:controls=true|linenumbers=true}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" >    @Inject <br>	@Traditional
@Primary <br>	private ComponentEventResultProcessor processor; <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">{<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">new</span>code}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" > <br>The two marker annotations,
{{Traditional}} and {{Primary}}, ensure that only a single service matches. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
        </table>
</div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <h2><a name="InjectionFAQ-Injection"></a>Injection</h2>

<h3><a name="InjectionFAQ-What%27sthedifferencebetweenthe%7B%7BComponent%7D%7Dand%7B%7BInjectComponent%7D%7Dannotations%3F"></a>What's
the difference between the <tt>Component</tt> and <tt>InjectComponent</tt>
annotations?</h3>

<p>The <tt>Component</tt> annotation is used to define the <em>type</em>
of component, and its parameter bindings. When using <tt>Component</tt>, the template<br/>
must not define the type, and any parameter bindings are merged in:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
  &lt;a t:id=<span class="code-quote">"home"</span> class=<span class="code-quote">"nav"</span>&gt;Back
to home&lt;/a&gt;
</pre>
</div></div>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
  @Component(parameters={ <span class="code-quote">"page=index"</span> })
  <span class="code-keyword">private</span> PageLink home;
</pre>
</div></div>

<p>Here the type of component is defined by the field type. The field name is matched
against the <tt>t:id</tt> in the template. The <tt>page</tt> parameter<br/>
is set in the Java class, and the informal <tt>class</tt> parameter is set in
the template.  If the tag in the template was <tt>&lt;t:pagelink&gt;</tt>,<br/>
or if the template tag included the attribute <tt>t:type="pagelink"</tt>, then
you would see an exception.</p>

<p>By contrast, <tt>InjectComponent</tt> expects the component to be already
defined, and doesn't allow any configuration of it:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
  &lt;t:form t:id=<span class="code-quote">"login"</span>&gt; .... &lt;/t:form&gt;
</pre>
</div></div>

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

<p>Again, we're matching the field name to the component id, and you would get an error
if the component is not defined in the template.</p>

<h3><a name="InjectionFAQ-What%27sthedifferencebetweenthe%7B%7BInjectPage%7D%7Dand%7B%7BInjectContainer%7D%7Dannotations%3F"></a>What's
the difference between the <tt>InjectPage</tt> and <tt>InjectContainer</tt>
annotations?</h3>

<p>The <tt>InjectPage</tt> annotation is used to inject some page in the
application into a field of some other page.  You often see it used from event handler methods:</p>

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

  <span class="code-object">Object</span> onSuccessFromRegistrationForm()
  {
    confirmRegistration.setStatus(<span class="code-quote">"Registration accepted"</span>);
    confirmRegistration.setValidationCode(userRegistrationData.getValidationCode());

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

<p>This code pattern is used to configure peristent properties of a page before returning
it; Tapestry will send a client redirect to the page to present<br/>
the data.</p>

<p><tt>InjectContainer</tt> can be used inside a component or a mixin. 
In a component, it injects the immediate container of the component; this is often the top-level
page object.</p>

<p>In a mixin, it injects the component to which the mixin is attached.</p>

<h3><a name="InjectionFAQ-IgetanexceptionbecauseIhavetwoserviceswiththesameinterface%2ChowdoIhandlethis%3F"></a>I
get an exception because I have two services with the same interface, how do I handle this?</h3>

<p>It's not uncommon to have two or more services that implement the exact same interface.
When you inject, you might start by just identifying the<br/>
type of service to inject:</p>

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

<p>Which results in the error: <b>Service interface org.apache.tapestry5.services.ComponentEventResultProcessor
is matched by 3 services: AjaxComponentEventResultProcessor, ComponentEventResultProcessor,
ComponentInstanceResultProcessor. Automatic dependency resolution requires that exactly one
service implement the interface.</b></p>

<p>We need more information than just the service interface type in order to identify
which of the three services to inject. One possibility is to inject with the correct service
id:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
	@InjectService(<span class="code-quote">"ComponentEventResultProcessor"</span>)
	<span class="code-keyword">private</span> ComponentEventResultProcessor processor;
</pre>
</div></div>

<p>This works ... but it is clumsy. If the service id, "ComponentEventResultProcessor",
ever changes, this code will break. It's not <em>refactoring safe</em>.</p>

<p>Instead, we should use marker annotations.  If we look at <tt>TapestryModule</tt>,
where the ComponentEventResultProcessor service is defined, we'll see it identifies the necessary
markers:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
    @Marker(
    { Primary.class, Traditional.class })
    <span class="code-keyword">public</span> ComponentEventResultProcessor buildComponentEventResultProcessor(
            Map&lt;<span class="code-object">Class</span>, ComponentEventResultProcessor&gt;
configuration)
    {
        <span class="code-keyword">return</span> constructComponentEventResultProcessor(configuration);
    }
</pre>
</div></div>

<p>When a service has marker annotations, the annotations present at the <em>point
of injection</em> (the field, method parameter, or constructor parameter) are used to
select a matching service.  The list of services that match by type is then filtered to only
include services that have all of the marker annotations present at the point of injection.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
    @Inject
	@Traditional @Primary
	<span class="code-keyword">private</span> ComponentEventResultProcessor processor;
</pre>
</div></div>

<p>The two marker annotations, <tt>Traditional</tt> and <tt>Primary</tt>,
ensure that only a single service matches.</p>

<h3><a name="InjectionFAQ-What%27sthedifferencebetween%7B%7BInject%7D%7Dand%7B%7BEnvironmental%7D%7D%3F"></a>What's
the difference between <tt>Inject</tt> and <tt>Environmental</tt>?</h3>

<p><tt>Inject</tt> is relatively general; it can be used to inject resources
specific to a page or component (such as ComponentResources, Logger, or Messages), or it can
inject services or other objects obtained from the Tapestry IoC container.  Once the page
is loaded, the values for these injections never change.</p>

<p><tt>Environmental</tt> is different; it exposes a request-scoped, dynamically
bound value<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>

<ul>
	<li>Request scoped: different threads (processing different requests) will see different
values when reading the field.</li>
	<li>Dynamically bound: the value is explicitly placed into the Environment, and can
be overridden at any time.</li>
</ul>


<p>Environmentals are a form of loosely connected communication between an outer component
(or even a service) and an inner component.  Example: the Form<br/>
component places a <tt>FormSupport</tt> object into the environment.  Other components,
such as TextField, use the <tt>FormSupport</tt> when rendering to perform functions
such as allocate unique control names or register client-side validations.  The TextField
doesn't require that the Form component be the immediate container component, or even an ancestor:
a Form on one page may, indirectly, communicate with a TextField on some entirely different
page. Neither component directly links to the other, the <tt>FormSupport</tt>
is the conduit that connects them.</p>

<hr />
<p><table class='Footnotes' style='width: 100%; border:none;' cellspacing='0' cellpadding='0'
summary='This table contains one or more notes for references made elsewhere on the page.'>
  <caption class='accessibility'>Footnotes</caption>
  <thead class='accessibility'>
    <tr class='accessibility'>
      <th class='accessibility' id='footnote-th1'>Reference</th>
      <th class='accessibility' id='footnote-th2'>Notes</th>
    </tr>
  </thead>
  <tbody>
    <tr name='Footnote1'>
      <td valign='top' class='FootnoteNum' headings='footnote-th1'>
        <a href='#FootnoteMarker1'
          onClick='footnoteMarkerHighlight("1");'
          onMouseOver='footnoteHighlight("1",false);'
          alt='Footnote: Click to return to reference in text'
          title='Footnote: Click to return to reference in text'
          id='FootnoteNum1'>
            1
        </a>
      </td>
      <td id='Footnote1'
        valign='top'
        width='100%'
        class='Footnote'
        headings='footnote-th2'>
          Environmental was chosen as the value "comes from the environment", whatever that
means. A name more evocative of its function still has not occurred to the Tapestry team!
      </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/Injection+FAQ">View
Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=23335313&revisedVersion=6&originalVersion=5">View
Changes</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message