tapestry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Tapestry > IoC - configuration
Date Wed, 01 Dec 2010 02:36: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/IoC+-+configuration">IoC
- configuration</a></h2>
    <h4>Page <b>edited</b> by             <a href="https://cwiki.apache.org/confluence/display/~bobharner">Bob
Harner</a>
    </h4>
        <div id="versionComment">
        <b>Comment:</b>
        Fixed most broken links, some misspelllings<br />
    </div>
        <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" >  }  {code} <br> <br></td></tr>
            <tr><td class="diff-changed-lines" >The *extensibility* comes from
the fact <span class="diff-added-words"style="background-color: #dfd;">that</span>
multiple modules may all contribute to the same service configuration: <br></td></tr>
            <tr><td class="diff-unchanged" > <br>{code:java} <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >Now the FileServicerDispatcher builder
method gets a Map with at least four entries in it. <br> <br></td></tr>
            <tr><td class="diff-changed-lines" >Because Tapestry IoC is highly
dynamic (it scans the visible JAR manifest files to identify module classes), the FileServicerDispatcher
service may be in one module, and the other contributing modules (such as the one that contributes
the Office file services) may be written at a much later date. With no change to the FileServicerDispatcher
service or its module class, the new services &quot;plug into&quot; the overall solution,
simply by having their JAR&#39;s on <span class="diff-added-words"style="background-color:
#dfd;">the</span> runtime classpath. <br></td></tr>
            <tr><td class="diff-unchanged" > <br>h1. Naming conventions
vs. Annotations <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >{since:since=5.2}{since} <br>
<br></td></tr>
            <tr><td class="diff-changed-lines" >If you prefer annotations over
naming conventions you can use the <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">[Contribute|#apidocsorgapachetapestry5iocannotationsContribute.html]</span>
<span class="diff-added-words"style="background-color: #dfd;">@[Contribute|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Contribute.html]</span>
annotation. As of version 5.2 this annotation that may be placed on a contributor method of
a module instead of starting the methods name with &quot;contribute&quot;. The value
of the annotation is the type of the service to contribute into. <br></td></tr>
            <tr><td class="diff-unchanged" > <br>The primary reasons to
use @Contribute and marker annotations is twofold: <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >{code} <br> <br></td></tr>
            <tr><td class="diff-changed-lines" >In this example, the method will
only be invoked when constructing a service configuration where the service itself has both
the Red and Blue marker annotations.  Tapestry knows which annotations are marker annotations,
and which marker annotations apply to the service, via the <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">[Marker|#apidocsorgapachetapestry5iocannotationsMarker.html]</span>
<span class="diff-added-words"style="background-color: #dfd;">@[Marker|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Marker.html]</span>
annotation on the service implementation. <br></td></tr>
            <tr><td class="diff-unchanged" > <br></td></tr>
            <tr><td class="diff-changed-lines" >If the special <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">[Local|#apidocsorgapachetapestry5iocannotationsLocal.html]</span>
<span class="diff-added-words"style="background-color: #dfd;">@[Local|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Local.html]</span>
annotation is present, then contribution is made only to the configuration of a service being
constructed in the same module. <br></td></tr>
            <tr><td class="diff-unchanged" > <br>It is not impossible that
the same contribution method will be invoked to contribute to the configuration of multiple
different services. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >Here we don&#39;t even need a
separate class for the implementation, we use a inner class for the implementation. The point
is, the configuration is provided to the builder method, which passes it along to the implementation
of the service. <br> <br></td></tr>
            <tr><td class="diff-changed-lines" >On the contribution side, a service
contribution method sees a <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">[Configuration|#apidocsorgapachetapestry5iocConfiguration.html]</span>
<span class="diff-added-words"style="background-color: #dfd;">[Configuration|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/Configuration.html]</span>
object: <br></td></tr>
            <tr><td class="diff-unchanged" > <br>{code:java} <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >  }    {code} <br> <br></td></tr>
            <tr><td class="diff-changed-lines" >The Configuration interface defines
just a single method: <span class="diff-changed-words"><span class="diff-added-chars"style="background-color:
#dfd;">{{</span>add()<span class="diff-added-chars"style="background-color: #dfd;">}}</span>.</span>
This is very intentional: the only thing you can do is add new items. If we passed in a Collection,
you might be tempted to check it for values, or remove them ... but that flys in the face
of the fact that the order of execution of these service contribution methods is entirely
unknown. <br></td></tr>
            <tr><td class="diff-unchanged" > <br>For readability (if Java
any longer supports that concept), we&#39;ve parameterized the configuration parameter
of the method, constraining it to instances of java.lang.Runnable, so as to match the corresponding
parameter. This is optional, but often very helpful. In any case, attempting to contribute
an object that doesn&#39;t extend or implement the type (Runnable) will result in a runtime
warning (and the value will be ignored). <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >Ordered lists are much more common.
With an ordered list, the contributions are sorted into a proper order before being provided
to the service builder method. <br> <br></td></tr>
            <tr><td class="diff-changed-lines" >Again, the order in which service
contribution methods are invoked is unknown. Therefore, the order in which objects are added
to the configuration is not known. Instead, we enforce an order on the items _after_ all the
contributions have been added. As with [service <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">decorators|#decorator.html],</span>
<span class="diff-added-words"style="background-color: #dfd;">decorators|IoC - decorator],</span>
we set the order by giving each contributed object a unique id, and identifying (by id) which
items must preceded it in the list, and which must follow. <br></td></tr>
            <tr><td class="diff-unchanged" > <br>So, if we changed our Startup
service to require a specific order for startup: <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >Notice that the service builder method
is shielded from the details of how the items are ordered. It doesn&#39;t have to know
about ids and pre- and post-requisites. By using a parameter type of List, we&#39;ve triggered
Tapestry to collected all the ordering information. <br> <br></td></tr>
            <tr><td class="diff-changed-lines" >For our service contribution methods,
we must provide a parameter of type <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">[OrderedConfiguration|#apidocsorgapachetapestry5iocOrderedConfiguration.html]:</span>
<span class="diff-added-words"style="background-color: #dfd;">[OrderedConfiguration|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/OrderedConfiguration.html]:</span>
<br></td></tr>
            <tr><td class="diff-unchanged" > <br>{code:java} <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >Often, you don&#39;t care about
ordering, the first form of the add method is used then. The ordering algorithm will find
a spot for the object (here the JMSStartup instance) based on the constraints of other contributed
objects. <br> <br></td></tr>
            <tr><td class="diff-changed-lines" >For the &quot;FileSystem&quot;
contribution, a constraint has been specified, indicating that FileSystem should be ordered
after some other contribution named &quot;CacheSetup&quot;. Any number of such [ordering
<span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">constraints|#order.html]</span>
<span class="diff-added-words"style="background-color: #dfd;">constraints|IoC - order]</span>
may be specified (the <span class="diff-changed-words"><span class="diff-added-chars"style="background-color:
#dfd;">{{</span>add()<span class="diff-added-chars"style="background-color: #dfd;">}}</span></span>
method accepts a variable number of arguments). <br></td></tr>
            <tr><td class="diff-unchanged" > <br>The object passed in may
be null; this is valid, and is considered a &quot;join point&quot;: points of reference
in the list that don&#39;t actually have any meaning of their own, but can be used when
ordering other elements. _TODO: Show example for chain of command, once that&#39;s put
together._ <br> <br></td></tr>
            <tr><td class="diff-changed-lines" >Null values, once ordered, are
<span class="diff-changed-words">edit<span class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">t</span>ed</span>
out (the List passed to the service builder method does not include any nulls). Again, they
are allowed as placeholders, for the actual contributed objects to organize themselves around.
<br></td></tr>
            <tr><td class="diff-unchanged" > <br>h2. Mapped Configurations
<br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >Neither the key nor the value may
be null. <br> <br></td></tr>
            <tr><td class="diff-changed-lines" >For mapped configurations where
the key type is String, a <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">[CaseInsensitiveMap|#apidocsorgapachetapestry5iocutilCaseInsensitiveMap.html]</span>
<span class="diff-added-words"style="background-color: #dfd;">[CaseInsensitiveMap|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/util/CaseInsensitiveMap.html]</span>
will be automatically used (and passed to the service builder method), to help ensure that
[case insensitivity|#case.html] is automatic and pervasive. <br></td></tr>
            <tr><td class="diff-unchanged" > <br>h1. Injecting Classes <br>
<br></td></tr>
            <tr><td class="diff-changed-lines" >All three configuration interfaces
have a second method, <span class="diff-changed-words"><span class="diff-added-chars"style="background-color:
#dfd;">{{</span>addInstance()<span class="diff-added-chars"style="background-color:
#dfd;">}}</span>.</span> This method takes a class, not an instance. The class
is instantiated and contributed. If the constructor for the class takes dependencies, those
are injected as well. <br></td></tr>
            <tr><td class="diff-unchanged" > <br>h1. Injecting Resources
<br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >In addition to injecting services
into a contributor method (via the @InjectService and @Inject annotations), Tapestry will
key off of the parameter type to allow other things to be injected. <br> <br></td></tr>
            <tr><td class="diff-changed-lines" >* <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">[ObjectLocator|#apidocsorgapachetapestry5iocObjectLocator.html]:</span>
<span class="diff-added-words"style="background-color: #dfd;">[ObjectLocator|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/ObjectLocator.html]:</span>
access to other services visible to the contributing module <br></td></tr>
            <tr><td class="diff-unchanged" >* org.slf4j.Logger: the Logger for
the service being contributed to <br>No annotation is needed for these cases. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >h1. Configuration Overrides <br>
<br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">*New
in 5.1* The OrderedConfiguration and MappedConfiguration interfaces now support overrides.
An override is a replacement for a normally contributed object. An override _must_ match a
contributed object, and each contributed object may be overidden at most once. <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">{since:since=5.1}{since}
<br></td></tr>
            <tr><td class="diff-unchanged" > <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">The
OrderedConfiguration and MappedConfiguration interfaces now support overrides. An override
is a replacement for a normally contributed object. An override _must_ match a contributed
object, and each contributed object may be overridden at most once. <br> <br></td></tr>
            <tr><td class="diff-unchanged" >The new object replaces the original
object; alternately, you may override the original object with null. <br> <br>This
allows you to fine tune configuration values that are contributed from modules that you are
using, rather than just those that you write yourself. It is powerful and a bit dangerous.
<br> <br></td></tr>
            <tr><td class="diff-changed-lines" >In Tapestry 5.0, services that
wanted to support this kind of override behavior had to implement it on an ad-hoc basis, such
as ApplicationDefaults overriding FactoryDefaults. In many cases, that is <span class="diff-changed-words">stil<span
class="diff-added-chars"style="background-color: #dfd;">l</span></span> useful.
<br></td></tr>
        </table>
</div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <h1><a name="IoC-configuration-TapestryIoCConfigurations"></a>Tapestry
IoC Configurations</h1>

<p>One of the key concepts on Tapestry IoC is <em>distributed configuration</em>.
This is a concept borrowed from the Eclipse Plugin API and evidenced in Apache HiveMind prior
to Tapestry 5 IoC.</p>

<p>So ... nice term, what does it mean?</p>

<p>Distributed configuration is the key feature of Tapestry IoC that supports <em>extensibility</em>
and <em>modularity</em>.</p>

<p>The distributed part refers to the fact that <em>any module</em> may
make <em>contributions</em> to any service's configuration.</p>

<p>This seems esoteric, but is quite handy, and is best explained by example.</p>

<p>Say you are building a service that, say, maps a file extension to an interface called
FileServicer. There's a bunch of different services, all implementing the FileServicer interface,
across many different modules, each doing something specific for a particular type of file
(identified by the file's extension).</p>

<p>A central service uses this configuration to select a particular FileService interface:</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>
FileServiceDispatcher buildFileServicerDispatcher(Map&lt;<span class="code-object">String</span>,FileServicer&gt;
contributions)
  {
    <span class="code-keyword">return</span> <span class="code-keyword">new</span>
FileServiceDispatcherImpl(contributions);
  } </pre>
</div></div>

<p>In order to provide a value for the contribution parameter, Tapestry will <em>collect</em>
contributions from service contribution methods. It will ensure that the keys and values match
the generic types shown (String for the key, FileServicer for the value). The map will be
assembled and passed into the service builder method, and from there, into the FileServiceDispatcherImpl
constructor.</p>

<p>So where do the values come from? Service contributor methods, methods that start
with "contribute":</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 contributeFileServicerDispatcher(MappedConfiguration&lt;<span class="code-object">String</span>,FileServicer&gt;
configuration)
  {
    configuration.add(<span class="code-quote">"txt"</span>, <span class="code-keyword">new</span>
TextFileServicer());
    configuration.add(<span class="code-quote">"pdf"</span>, <span class="code-keyword">new</span>
PDFFileServicer());
  }  </pre>
</div></div>

<p>Like service builder and service decorator methods, we can inject services if we
like:</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 contributeFileServicerDispatcher(MappedConfiguration&lt;<span class="code-object">String</span>,FileServicer&gt;
configuration,
  
    @InjectService(<span class="code-quote">"TextFileServicer"</span>) FileServicer
textFileServicer,
    
    @InjectService(<span class="code-quote">"PDFFileServicer"</span>) FileServicer
pdfFileServicer,
  {
    configuration.add(<span class="code-quote">"txt"</span>, textFileServicer);
    configuration.add(<span class="code-quote">"pdf"</span>, pdfFileServicer);
  }  </pre>
</div></div>

<p>The <b>extensibility</b> comes from the fact that multiple modules may
all contribute to the same service configuration:</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 contributeFileServicerDispatcher(MappedConfiguration&lt;<span class="code-object">String</span>,FileServicer&gt;
configuration)
  {
    configuration.add(<span class="code-quote">"doc"</span>, <span class="code-keyword">new</span>
WordFileServicer());
    configuration.add(<span class="code-quote">"ppt"</span>, <span class="code-keyword">new</span>
PowerPointFileServicer());
  }  </pre>
</div></div>

<p>Now the FileServicerDispatcher builder method gets a Map with at least four entries
in it.</p>

<p>Because Tapestry IoC is highly dynamic (it scans the visible JAR manifest files to
identify module classes), the FileServicerDispatcher service may be in one module, and the
other contributing modules (such as the one that contributes the Office file services) may
be written at a much later date. With no change to the FileServicerDispatcher service or its
module class, the new services "plug into" the overall solution, simply by having their JAR's
on the runtime classpath.</p>

<h1><a name="IoC-configuration-Namingconventionsvs.Annotations"></a>Naming
conventions vs. Annotations</h1>



<div class='panelMacro'><table class='infoMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/information.gif" width="16"
height="16" align="absmiddle" alt="" border="0"></td><td><b>Added in
5.2</b><br /></td></tr></table></div>
<div style="border-right: 20px solid #D8E4F1;border-left: 20px solid #D8E4F1;"></div>

<p>If you prefer annotations over naming conventions you can use the @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Contribute.html"
class="external-link" rel="nofollow">Contribute</a> annotation. As of version 5.2
this annotation that may be placed on a contributor method of a module instead of starting
the methods name with "contribute". The value of the annotation is the type of the service
to contribute into. </p>

<p>The primary reasons to use @Contribute and marker annotations is twofold:</p>

<ul>
	<li>There is no longer a linkage between the contribution method name and the service
id, which is much more refactoring safe: if you change the service interface name, or the
id of the service, your method will still be invoked when using @Contribute.</li>
</ul>


<ul>
	<li>It makes it much easier for an <span class="error">&#91;override|cookbook/override.html&#93;</span>
of the service to get the configuration intended for the original service.</li>
</ul>


<p>The following example is an alternative for the contribution method above.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
@Contribute(FileServiceDispatcher.class)
<span class="code-keyword">public</span> <span class="code-keyword">static</span>
void arbitraryMethodName(MappedConfiguration&lt;<span class="code-object">String</span>,FileServicer&gt;
configuration)
{
    configuration.add(<span class="code-quote">"doc"</span>, <span class="code-keyword">new</span>
WordFileServicer());
    configuration.add(<span class="code-quote">"ppt"</span>, <span class="code-keyword">new</span>
PowerPointFileServicer());
}  
</pre>
</div></div>

<p>If you have several implementations of a service interface, you have to disambiguate
the services. For this purpose the marker  annotations should be placed on the contributor
method.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
@Contribute(FileServiceDispatcher.class)
@Red @Blue
<span class="code-keyword">public</span> <span class="code-keyword">static</span>
void arbitraryMethodName(MappedConfiguration&lt;<span class="code-object">String</span>,FileServicer&gt;
configuration)
{
    configuration.add(<span class="code-quote">"doc"</span>, <span class="code-keyword">new</span>
WordFileServicer());
    configuration.add(<span class="code-quote">"ppt"</span>, <span class="code-keyword">new</span>
PowerPointFileServicer());
}
</pre>
</div></div>

<p>In this example, the method will only be invoked when constructing a service configuration
where the service itself has both the Red and Blue marker annotations.  Tapestry knows which
annotations are marker annotations, and which marker annotations apply to the service, via
the @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Marker.html"
class="external-link" rel="nofollow">Marker</a> annotation on the service implementation.</p>

<p>If the special @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Local.html"
class="external-link" rel="nofollow">Local</a> annotation is present, then contribution
is made only to the configuration of a service being constructed in the same module.</p>

<p>It is not impossible that the same contribution method will be invoked to contribute
to the configuration of multiple different services.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
  @Contribute(FileServiceDispatcher.class)
  @Local
  <span class="code-keyword">public</span> <span class="code-keyword">static</span>
void arbitraryMethodName(MappedConfiguration&lt;<span class="code-object">String</span>,FileServicer&gt;
configuration)
  {
    configuration.add(<span class="code-quote">"doc"</span>, <span class="code-keyword">new</span>
WordFileServicer());
    configuration.add(<span class="code-quote">"ppt"</span>, <span class="code-keyword">new</span>
PowerPointFileServicer());
  }  
</pre>
</div></div>

<h1><a name="IoC-configuration-ConfigurationTypes"></a>Configuration Types</h1>

<p>There are three different styles of configurations (with matching contributions):</p>

<ul>
	<li>Unordered Collection. Contributions are simply added in and order is not important.</li>
	<li>Ordered List. Contributions are provided as an ordered list. Contributions must
establish the order by giving each contributed object a unique id, by establishing forward
and backward dependencies between the values.</li>
	<li>Map. Contributions provide unique keys and corresponding values.</li>
</ul>


<h2><a name="IoC-configuration-UnorderedCollection"></a>Unordered Collection</h2>

<p>A service builder method can collect an unordered list of values by defining a parameter
of type java.util.Collection. Further, you should parameterize the type of collection. Tapestry
will identify the parameterized type and ensure that all contributions match.</p>

<p>One thing to remember is that the order in which contributions occur is unspecified.
There will be a possibly large number modules, each having zero or more methods that contribute
into the service. The order in which these methods are invoked is unknown.</p>

<p>For example, here's a kind of Startup service that needs some Runnable objects. It
doesn't care what order the Runnable objects are executed in.</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>
<span class="code-object">Runnable</span> buildStartup(<span class="code-keyword">final</span>
Collection&lt;<span class="code-object">Runnable</span>&gt; configuration)
  {
    <span class="code-keyword">return</span> <span class="code-keyword">new</span>
<span class="code-object">Runnable</span>()
    {
      <span class="code-keyword">public</span> void run()
      {
        <span class="code-keyword">for</span> (<span class="code-object">Runnable</span>
contribution : configuration)
          contribution.run();
      }
    };
  }  </pre>
</div></div>

<p>Here we don't even need a separate class for the implementation, we use a inner class
for the implementation. The point is, the configuration is provided to the builder method,
which passes it along to the implementation of the service.</p>

<p>On the contribution side, a service contribution method sees a <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/Configuration.html"
class="external-link" rel="nofollow">Configuration</a> object:</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 contributeStartup(Configuration&lt;<span class="code-object">Runnable</span>&gt;
configuration)
  {
    configuration.add(<span class="code-keyword">new</span> JMSStartup());
    configuration.add(<span class="code-keyword">new</span> FileSystemStartup());
  }    </pre>
</div></div>

<p>The Configuration interface defines just a single method: <tt>add()</tt>.
This is very intentional: the only thing you can do is add new items. If we passed in a Collection,
you might be tempted to check it for values, or remove them ... but that flys in the face
of the fact that the order of execution of these service contribution methods is entirely
unknown.</p>

<p>For readability (if Java any longer supports that concept), we've parameterized the
configuration parameter of the method, constraining it to instances of java.lang.Runnable,
so as to match the corresponding parameter. This is optional, but often very helpful. In any
case, attempting to contribute an object that doesn't extend or implement the type (Runnable)
will result in a runtime warning (and the value will be ignored).</p>

<p>Tapestry supports only this simple form of parameterized types. Java generics supports
a wider form, "wildcards", that Tapestry doesn't understand.</p>

<h2><a name="IoC-configuration-OrderedList"></a><a name="IoC-configuration-OrderedList"></a>Ordered
List</h2>

<p>Ordered lists are much more common. With an ordered list, the contributions are sorted
into a proper order before being provided to the service builder method.</p>

<p>Again, the order in which service contribution methods are invoked is unknown. Therefore,
the order in which objects are added to the configuration is not known. Instead, we enforce
an order on the items <em>after</em> all the contributions have been added. As
with <a href="/confluence/display/TAPESTRY/IoC+-+decorator" title="IoC - decorator">service
decorators</a>, we set the order by giving each contributed object a unique id, and
identifying (by id) which items must preceded it in the list, and which must follow.</p>

<p>So, if we changed our Startup service to require a specific order for startup:</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>
<span class="code-object">Runnable</span> buildStartup(<span class="code-keyword">final</span>
List&lt;<span class="code-object">Runnable</span>&gt; configuration)
  {
    <span class="code-keyword">return</span> <span class="code-keyword">new</span>
<span class="code-object">Runnable</span>()
    {
      <span class="code-keyword">public</span> void run()
      {
        <span class="code-keyword">for</span> (<span class="code-object">Runnable</span>
contribution : configuration)
          contribution.run();
      }
    };
  }  </pre>
</div></div>

<p>Notice that the service builder method is shielded from the details of how the items
are ordered. It doesn't have to know about ids and pre- and post-requisites. By using a parameter
type of List, we've triggered Tapestry to collected all the ordering information.</p>

<p>For our service contribution methods, we must provide a parameter of type <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/OrderedConfiguration.html"
class="external-link" rel="nofollow">OrderedConfiguration</a>:</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 contributeStartup(OrderedConfiguration&lt;<span class="code-object">Runnable</span>&gt;
configuration)
  {
    configuration.add(<span class="code-quote">"JMS"</span>, <span class="code-keyword">new</span>
JMSStartup());
    configuration.add(<span class="code-quote">"FileSystem"</span>, <span class="code-keyword">new</span>
FileSystemStartup(), <span class="code-quote">"after:CacheSetup"</span>);
  }    </pre>
</div></div>

<p>Often, you don't care about ordering, the first form of the add method is used then.
The ordering algorithm will find a spot for the object (here the JMSStartup instance) based
on the constraints of other contributed objects.</p>

<p>For the "FileSystem" contribution, a constraint has been specified, indicating that
FileSystem should be ordered after some other contribution named "CacheSetup". Any number
of such <a href="/confluence/display/TAPESTRY/IoC+-+order" title="IoC - order">ordering
constraints</a> may be specified (the <tt>add()</tt> method accepts a variable
number of arguments).</p>

<p>The object passed in may be null; this is valid, and is considered a "join point":
points of reference in the list that don't actually have any meaning of their own, but can
be used when ordering other elements. <em>TODO: Show example for chain of command, once
that's put together.</em></p>

<p>Null values, once ordered, are edited out (the List passed to the service builder
method does not include any nulls). Again, they are allowed as placeholders, for the actual
contributed objects to organize themselves around.</p>

<h2><a name="IoC-configuration-MappedConfigurations"></a>Mapped Configurations</h2>

<p>As discussed in the earlier examples, mapped configurations are also supported. The
keys passed in must be unique. When conflicts occur, Tapestry will log warnings (identifying
the source, in terms of invoked methods, of the conflict), and ignore the conflicting value.</p>

<p>Neither the key nor the value may be null.</p>

<p>For mapped configurations where the key type is String, a <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/util/CaseInsensitiveMap.html"
class="external-link" rel="nofollow">CaseInsensitiveMap</a> will be automatically
used (and passed to the service builder method), to help ensure that <a href="#IoC-configuration-case.html">case
insensitivity</a> is automatic and pervasive.</p>

<h1><a name="IoC-configuration-InjectingClasses"></a>Injecting Classes</h1>

<p>All three configuration interfaces have a second method, <tt>addInstance()</tt>.
This method takes a class, not an instance. The class is instantiated and contributed. If
the constructor for the class takes dependencies, those are injected as well.</p>

<h1><a name="IoC-configuration-InjectingResources"></a>Injecting Resources</h1>

<p>In addition to injecting services into a contributor method (via the @InjectService
and @Inject annotations), Tapestry will key off of the parameter type to allow other things
to be injected.</p>

<ul>
	<li><a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/ObjectLocator.html"
class="external-link" rel="nofollow">ObjectLocator</a>: access to other services
visible to the contributing module</li>
	<li>org.slf4j.Logger: the Logger for the service being contributed to<br/>
No annotation is needed for these cases.</li>
</ul>


<h1><a name="IoC-configuration-ConfigurationOverrides"></a>Configuration
Overrides</h1>



<div class='panelMacro'><table class='infoMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/information.gif" width="16"
height="16" align="absmiddle" alt="" border="0"></td><td><b>Added in
5.1</b><br /></td></tr></table></div>
<div style="border-right: 20px solid #D8E4F1;border-left: 20px solid #D8E4F1;"></div>

<p>The OrderedConfiguration and MappedConfiguration interfaces now support overrides.
An override is a replacement for a normally contributed object. An override <em>must</em>
match a contributed object, and each contributed object may be overridden at most once.</p>

<p>The new object replaces the original object; alternately, you may override the original
object with null.</p>

<p>This allows you to fine tune configuration values that are contributed from modules
that you are using, rather than just those that you write yourself. It is powerful and a bit
dangerous.</p>

<p>In Tapestry 5.0, services that wanted to support this kind of override behavior had
to implement it on an ad-hoc basis, such as ApplicationDefaults overriding FactoryDefaults.
In many cases, that is still useful.</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/IoC+-+configuration">View
Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=23338480&revisedVersion=5&originalVersion=4">View
Changes</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message