Author: buildbot
Date: Mon Feb 19 03:21:26 2018
New Revision: 1025579
Log:
Production update by buildbot for tapestry
Modified:
websites/production/tapestry/content/cache/main.pageCache
websites/production/tapestry/content/google-app-engine.html
websites/production/tapestry/content/ioc-cookbook-overriding-ioc-services.html
websites/production/tapestry/content/ioc-cookbook-patterns.html
websites/production/tapestry/content/ioc-cookbook-service-configurations.html
websites/production/tapestry/content/performance-and-clustering.html
websites/production/tapestry/content/using-select-with-a-list.html
Modified: websites/production/tapestry/content/cache/main.pageCache
==============================================================================
Binary files - no diff available.
Modified: websites/production/tapestry/content/google-app-engine.html
==============================================================================
--- websites/production/tapestry/content/google-app-engine.html (original)
+++ websites/production/tapestry/content/google-app-engine.html Mon Feb 19
03:21:26 2018
@@ -86,7 +86,7 @@
configuration.add(IOCSymbols.THREAD_POOL_ENABLED, "false");
...
     }</pre>
-</div></div><p>NOTE: Setting production mode to true is generally only
desirable in production and QA/testing environments, but you can override that
symbol with a JVM system property
(<code>-Dtapestry.production-mode=false</code>) in those environments.</p><h2
id="GoogleAppEngine-SerializableObjects">Serializable Objects</h2><ul><li>Make
sure all objects that you store in the servlet session -- particularly those
annotated with @SessionState and @SessionAttribute – are
actually serializable types. Otherwise you may see strange behavior in which
objects seem to lose their session values.</li></ul><h2
id="GoogleAppEngine-FileUploads">File Uploads</h2><ul><li>If you use the
Tapestry-upload module (or any file upload mechanism, for that matter), you'll
need to be sure that it is configured to <em>not</em> store temporary uploaded
files on the file system.  See <a href="google-app-engine.html">Google
App Engine</a> – particularly the <code>upload.reposit
ory-threshold</code> symbol, which should be set to a large
value.</li></ul><h2 id="GoogleAppEngine-OtherConsiderations">Other
Considerations</h2><p>Other than the above settings, Tapestry should provide no
impediment to running your app under Google App Engine. However, you still need
to adhere to all of GAE's usual constraints (as with any app, Tapestry or
otherwise). Please carefully read Google's documentation for general guidelines
for creating an app that is compatible with GAE.</p></div>
+</div></div><p>NOTE: Setting production mode to true is generally only
desirable in production and QA/testing environments, but you can override that
symbol with a JVM system property
(<code>-Dtapestry.production-mode=false</code>) in those environments.</p><h2
id="GoogleAppEngine-SerializableObjects">Serializable Objects</h2><ul><li>Make
sure all objects that you store in the servlet session -- particularly those
annotated with @SessionState and @SessionAttribute – are
actually serializable types. Otherwise you may see strange behavior in which
objects seem to lose their session values.</li></ul><h2
id="GoogleAppEngine-FileUploads">File Uploads</h2><ul><li>If you use the
Tapestry-upload module (or any file upload mechanism, for that matter), you'll
need to be sure that it is configured to <em>not</em> store temporary uploaded
files on the file system.  See <a
href="uploading-files.html">Uploading Files</a> – particularly the
<code>upload.repository-
threshold</code> symbol, which should be set to a large value.</li></ul><h2
id="GoogleAppEngine-OtherConsiderations">Other Considerations</h2><p>Other than
the above settings, Tapestry should provide no impediment to running your app
under Google App Engine. However, you still need to adhere to all of GAE's
usual constraints (as with any app, Tapestry or otherwise). Please carefully
read Google's documentation for general guidelines for creating an app that is
compatible with GAE.</p></div>
</div>
<div class="clearer"></div>
Modified:
websites/production/tapestry/content/ioc-cookbook-overriding-ioc-services.html
==============================================================================
---
websites/production/tapestry/content/ioc-cookbook-overriding-ioc-services.html
(original)
+++
websites/production/tapestry/content/ioc-cookbook-overriding-ioc-services.html
Mon Feb 19 03:21:26 2018
@@ -77,14 +77,14 @@
</div>
<div id="content">
- <div id="ConfluenceContent"><h1
id="IoCCookbook-OverridingIoCServices-OverridingTapestryIoCServices">Overriding
Tapestry IoC Services</h1><p>Tapestry is designed to be easy to customize, and
the IoC container is the key to that customizability.</p><p>One of Tapestry's
most important activities is resolving injected objects; that is, when Tapestry
is building an object or service and sees a constructor parameter or a field,
it must decide what value to plug in. Most of the time, the injected object is
a service defined elsewhere within the Tapestry IoC container.</p><p>However,
there are cases where you might want to override how Tapestry operates in some
specific way.</p><p>The strategy used to determine what object gets injected is
<a href="ioc-cookbook-overriding-ioc-services.html">defined inside Tapestry
IoC itself</a>; thus we can take advantage of several features of the Tapestry
IoC container in order to take control over specific injections.</p><h2
id="IoCCoo
kbook-OverridingIoCServices-ContributingaServiceOverride">Contributing a
Service Override</h2><p>In most cases, services are injected by matching just
the type; there is no @<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/InjectService.html">InjectService</a>
annotation, just a method or constructor parameter whose type matches the
service's interface.</p><p>In this case, it is very easy to supply your own
alternate implementation of a service, by <em>contributing</em><em> a Service
Override</em> in your module class (usually AppModule.java), like this:</p><div
class="code panel pdl" style="border-width: 1px;"><div class="codeHeader
panelHeader pdl" style="border-bottom-width: 1px;"><b>AppModule.java
(partial)</b></div><div class="codeContent panelContent pdl">
+ <div id="ConfluenceContent"><h1
id="IoCCookbook-OverridingIoCServices-OverridingTapestryIoCServices">Overriding
Tapestry IoC Services</h1><p>Tapestry is designed to be easy to customize, and
the IoC container is the key to that customizability.</p><p>One of Tapestry's
most important activities is resolving injected objects; that is, when Tapestry
is building an object or service and sees a constructor parameter or a field,
it must decide what value to plug in. Most of the time, the injected object is
a service defined elsewhere within the Tapestry IoC container.</p><p>However,
there are cases where you might want to override how Tapestry operates in some
specific way.</p><p>The strategy used to determine what object gets injected is
<a href="injection-in-detail.html">defined inside Tapestry IoC itself</a>;
thus we can take advantage of several features of the Tapestry IoC container in
order to take control over specific injections.</p><h2
id="IoCCookbook-OverridingI
oCServices-ContributingaServiceOverride">Contributing a Service
Override</h2><p>In most cases, services are injected by matching just the type;
there is no @<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/InjectService.html">InjectService</a>
annotation, just a method or constructor parameter whose type matches the
service's interface.</p><p>In this case, it is very easy to supply your own
alternate implementation of a service, by <em>contributing</em><em> a Service
Override</em> in your module class (usually AppModule.java), like this:</p><div
class="code panel pdl" style="border-width: 1px;"><div class="codeHeader
panelHeader pdl" style="border-bottom-width: 1px;"><b>AppModule.java
(partial)</b></div><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Contribute(ServiceOverride.class)
public static void
setupApplicationServiceOverrides(MappedConfiguration<Class,Object>
configuration)
{
configuration.addInstance(SomeServiceType.class,
SomeServiceTypeOverrideImpl.class);
}
</pre>
-</div></div><p>The name of the method is not important, as long as the @<a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Contribute.html">Contribute</a>
annotation is present on the method.</p><p>In this example, we are using
<code>addInstance()</code> which will instantiate the indicated class and
handle dependency resolution. (Be careful with this, because in some cases,
resolving dependencies of the override class can require checking against the
ServiceOverrides service, and you'll get a runtime exception about
ServiceOverrides requiring itself!).</p><p>Sometimes you'll want to define the
override as a service of its own. This is useful if you want to inject a Logger
specific to the service, or if the overriding implementation needs a <a
href="ioc-cookbook-overriding-ioc-services.html">service
configuration</a>:</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeHeader panelHeader pdl" style="
border-bottom-width: 1px;"><b>AppModule.java (partial)</b></div><div
class="codeContent panelContent pdl">
+</div></div><p>The name of the method is not important, as long as the @<a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Contribute.html">Contribute</a>
annotation is present on the method.</p><p>In this example, we are using
<code>addInstance()</code> which will instantiate the indicated class and
handle dependency resolution. (Be careful with this, because in some cases,
resolving dependencies of the override class can require checking against the
ServiceOverrides service, and you'll get a runtime exception about
ServiceOverrides requiring itself!).</p><p>Sometimes you'll want to define the
override as a service of its own. This is useful if you want to inject a Logger
specific to the service, or if the overriding implementation needs a <a
href="tapestry-ioc-configuration.html">service configuration</a>:</p><div
class="code panel pdl" style="border-width: 1px;"><div class="codeHeader
panelHeader pdl" style="border-bot
tom-width: 1px;"><b>AppModule.java (partial)</b></div><div class="codeContent
panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> public static void bind(ServiceBinder binder)
{
binder.bind(SomeServiceType.class,
SomeServiceTypeOverrideImpl.class).withId("SomeServiceTypeOverride");
@@ -96,7 +96,7 @@
configuration.add(SomeServiceType.class, override);
}
</pre>
-</div></div><p>Here we're defining a service using the module's
<code>bind()</code> method.</p><p>Every service in the IoC container must have
a unique id, that's why we used the <code>withId()</code> method; if we we
hadn't, the default service id would have been "SomeServiceType" which is a
likely conflict with the very service we're trying to override.</p><p>We can
inject our overriding implementation of SomeServiceType using the special @<a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Local.html">Local</a>
annotation, which indicates that a service within the same module only should
be injected (that is, services of the indicated type in other modules are
ignored). Without @Local, there would be a problem because the override
parameter would need to be resolved using the MasterObjectProvider and,
ultimately, the ServiceOverride service; this would cause Tapestry to throw an
exception indicating that ServiceOverride depe
nds on itself. We defuse that situation by using @Local, which prevents the
MasterObjectProvider service from being used to resolve the override
parameter.</p><h2
id="IoCCookbook-OverridingIoCServices-DecoratingServices">Decorating
Services</h2><p>Another option is to <a
href="ioc-cookbook-overriding-ioc-services.html">decorate</a> the existing
service. Perhaps you want to extend some of the behavior of the service but
keep the rest.</p><p>Alternately, this approach is useful to override a service
that is matched using marker annotations.</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>AppModule.java (partial)</b></div><div
class="codeContent panelContent pdl">
+</div></div><p>Here we're defining a service using the module's
<code>bind()</code> method.</p><p>Every service in the IoC container must have
a unique id, that's why we used the <code>withId()</code> method; if we we
hadn't, the default service id would have been "SomeServiceType" which is a
likely conflict with the very service we're trying to override.</p><p>We can
inject our overriding implementation of SomeServiceType using the special @<a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Local.html">Local</a>
annotation, which indicates that a service within the same module only should
be injected (that is, services of the indicated type in other modules are
ignored). Without @Local, there would be a problem because the override
parameter would need to be resolved using the MasterObjectProvider and,
ultimately, the ServiceOverride service; this would cause Tapestry to throw an
exception indicating that ServiceOverride depe
nds on itself. We defuse that situation by using @Local, which prevents the
MasterObjectProvider service from being used to resolve the override
parameter.</p><h2
id="IoCCookbook-OverridingIoCServices-DecoratingServices">Decorating
Services</h2><p>Another option is to <a
href="tapestry-ioc-decorators.html">decorate</a> the existing service. Perhaps
you want to extend some of the behavior of the service but keep the
rest.</p><p>Alternately, this approach is useful to override a service that is
matched using marker annotations.</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>AppModule.java (partial)</b></div><div
class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> public SomeServiceType decorateSomeServiceType(final
SomeServiceType delegate)
{
return new SomeServiceType() { . . . };
Modified: websites/production/tapestry/content/ioc-cookbook-patterns.html
==============================================================================
--- websites/production/tapestry/content/ioc-cookbook-patterns.html (original)
+++ websites/production/tapestry/content/ioc-cookbook-patterns.html Mon Feb 19
03:21:26 2018
@@ -128,7 +128,7 @@
</div>
-<p>The basis for these patterns is often the use of <em>service builder
methods</em>, where a <a href="ioc-cookbook-patterns.html">configuration</a>
for the service is combined with a factory to produce the service
implementation on the fly.</p><p><span class="confluence-anchor-link"
id="IoCCookbook-Patterns-chainofcommand"></span></p><h1
id="IoCCookbook-Patterns-ChainofCommandPattern">Chain of Command
Pattern</h1><p>Main Article: <a href="ioc-cookbook-patterns.html">Chain of
Command</a></p><p>Let's look at another example, again from the Tapestry code
base. The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/InjectionProvider.html">InjectProvider</a>
interface is used to process the @Inject annotation on the fields of a
Tapestry page or component. Many different instances are combined together to
form a <em>chain of command</em>.</p><p>The interface has only a single method
(this is far from uncommon):</p><div class="code p
anel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
+<p>The basis for these patterns is often the use of <em>service builder
methods</em>, where a <a
href="ioc-cookbook-service-configurations.html">configuration</a> for the
service is combined with a factory to produce the service implementation on the
fly.</p><p><span class="confluence-anchor-link"
id="IoCCookbook-Patterns-chainofcommand"></span></p><h1
id="IoCCookbook-Patterns-ChainofCommandPattern">Chain of Command
Pattern</h1><p>Main Article: <a href="chainbuilder-service.html">Chain of
Command</a></p><p>Let's look at another example, again from the Tapestry code
base. The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/InjectionProvider.html">InjectProvider</a>
interface is used to process the @Inject annotation on the fields of a
Tapestry page or component. Many different instances are combined together to
form a <em>chain of command</em>.</p><p>The interface has only a single method
(this is far from uncommon):</p><div
class="code panel pdl" style="border-width: 1px;"><div class="codeContent
panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public interface InjectionProvider
{
boolean provideInjection(String fieldName, Class fieldType, ObjectLocator
locator,
Modified:
websites/production/tapestry/content/ioc-cookbook-service-configurations.html
==============================================================================
---
websites/production/tapestry/content/ioc-cookbook-service-configurations.html
(original)
+++
websites/production/tapestry/content/ioc-cookbook-service-configurations.html
Mon Feb 19 03:21:26 2018
@@ -103,7 +103,7 @@
. . .
}</pre>
-</div></div><p>In many cases, the configuration is simply stored into an
instance variable; in this example, the value is transformed from a Collection
to a Set.</p><p>These kinds of unordered configurations are surprisingly rare
in Tapestry (the only other notable one is for the <a
href="ioc-cookbook-service-configurations.html">TypeCoercer</a> service).
However, as you can see, setting up such a configuration is quite easy.</p><h1
id="IoCcookbook-ServiceConfigurations-OrderedConfigurations">Ordered
Configurations</h1><p>Ordered configurations are very similar to unordered
configurations ... the difference is that the configuration is provided to the
service as a parameter of type List. This is used when the order of operations
counts. Often these configurations are related to a design pattern such as <a
href="ioc-cookbook-service-configurations.html">Chain of Command</a> or <a
href="ioc-cookbook-service-configurations.html">Pipeline</a>.</p><p>Here, the
example is the <a class
="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/Dispatcher.html">Dispatcher</a>
interface; a Dispatcher inside Tapestry is roughly equivalent to a servlet,
though a touch more active. It is passed a Request and decides if the URL for
the Request is something it can handle; if so it will process the request, send
a response, and return true.</p><p>Alternately, if the Request can't be
handled, the Dispatcher returns false.</p><div class="code panel pdl"
style="border-style: solid;border-width: 1px;"><div class="codeContent
panelContent pdl">
+</div></div><p>In many cases, the configuration is simply stored into an
instance variable; in this example, the value is transformed from a Collection
to a Set.</p><p>These kinds of unordered configurations are surprisingly rare
in Tapestry (the only other notable one is for the <a
href="ioc-cookbook-service-configurations.html">TypeCoercer</a> service).
However, as you can see, setting up such a configuration is quite easy.</p><h1
id="IoCcookbook-ServiceConfigurations-OrderedConfigurations">Ordered
Configurations</h1><p>Ordered configurations are very similar to unordered
configurations ... the difference is that the configuration is provided to the
service as a parameter of type List. This is used when the order of operations
counts. Often these configurations are related to a design pattern such as <a
href="chainbuilder-service.html">Chain of Command</a> or <a
href="pipelinebuilder-service.html">Pipeline</a>.</p><p>Here, the example is
the <a class="external-link" href="http
://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/Dispatcher.html">Dispatcher</a>
interface; a Dispatcher inside Tapestry is roughly equivalent to a servlet,
though a touch more active. It is passed a Request and decides if the URL for
the Request is something it can handle; if so it will process the request, send
a response, and return true.</p><p>Alternately, if the Request can't be
handled, the Dispatcher returns false.</p><div class="code panel pdl"
style="border-style: solid;border-width: 1px;"><div class="codeContent
panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public void
contributeMasterDispatcher(OrderedConfiguration<Dispatcher>
configuration, . . .)
{
// Looks for the root path and renders the start page
@@ -122,12 +122,12 @@
configuration.add("ComponentAction", new ComponentActionDispatcher(. . .),
"after:PageRender");
}</pre>
-</div></div><p>With an <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/OrderedConfiguration.html">OrderedConfiguration</a>,
each contribution gets a name, which must be unique. Here the names are
RootPath, Asset, PageRender and ComponentAction.</p><p>The add() method takes a
name, the contributed object for that name, and then zero or more optional
constraints. The constraints control the ordering. The "after:" constraint
ensures that the contribution is ordered after the other named contribution,
the "before:" contribution is the opposite.</p><p>The ordering occurs on the
complete set of contributions, from all modules.</p><p>Here, we need a specific
order, used to make sure that the Dispatchers don't get confused about which
URLs are appropriate ... for example, an asset URL might be
/assets/tapestry5/tapestry.js. This looks just like a component action URL (for
page "assets/tapestry5/tapestry" and component "js"). Given that sof
tware is totally lacking in basic common-sense, we instead use careful
ordering of the Dispatchers to ensure that AssetDispatcher is checked
<em>before</em> the ComponentAction dispatcher.</p><h2
id="IoCcookbook-ServiceConfigurations-ReceivingtheConfiguration.1">Receiving
the Configuration</h2><p>The configuration, once assembled and ordered, is
provided as a List.</p><p>The MasterDispatcher service configuration defines a
<a href="ioc-cookbook-service-configurations.html">Chain of Command</a> and we
can provide the implementation using virtually no code:</p><div class="code
panel pdl" style="border-style: solid;border-width: 1px;"><div
class="codeContent panelContent pdl">
+</div></div><p>With an <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/OrderedConfiguration.html">OrderedConfiguration</a>,
each contribution gets a name, which must be unique. Here the names are
RootPath, Asset, PageRender and ComponentAction.</p><p>The add() method takes a
name, the contributed object for that name, and then zero or more optional
constraints. The constraints control the ordering. The "after:" constraint
ensures that the contribution is ordered after the other named contribution,
the "before:" contribution is the opposite.</p><p>The ordering occurs on the
complete set of contributions, from all modules.</p><p>Here, we need a specific
order, used to make sure that the Dispatchers don't get confused about which
URLs are appropriate ... for example, an asset URL might be
/assets/tapestry5/tapestry.js. This looks just like a component action URL (for
page "assets/tapestry5/tapestry" and component "js"). Given that sof
tware is totally lacking in basic common-sense, we instead use careful
ordering of the Dispatchers to ensure that AssetDispatcher is checked
<em>before</em> the ComponentAction dispatcher.</p><h2
id="IoCcookbook-ServiceConfigurations-ReceivingtheConfiguration.1">Receiving
the Configuration</h2><p>The configuration, once assembled and ordered, is
provided as a List.</p><p>The MasterDispatcher service configuration defines a
<a href="chainbuilder-service.html">Chain of Command</a> and we can provide
the implementation using virtually no code:</p><div class="code panel pdl"
style="border-style: solid;border-width: 1px;"><div class="codeContent
panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> public static Dispatcher
buildMasterDispatcher(List<Dispatcher> configuration, ChainBuilder
chainBuilder)
{
return chainBuilder.build(Dispatcher.class, configuration);
}</pre>
-</div></div><p><a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/ChainBuilder.html">ChainBuilder</a>
is a service that <em>builds other services</em>. Here it creates an object of
type Dispatcher in terms of the list of Dispatchers. This is one of the most
common uses of service builder methods ... for when the service implementation
doesn't exist, but can be constructed at runtime.</p><h1
id="IoCcookbook-ServiceConfigurations-MappedConfigurations">Mapped
Configurations</h1><p>The last type of service configuration is the mapped
service configuration. Here we relate a key, often a string, to some value. The
contributions are ultimately combined to form a Map.</p><p>Tapestry IoC's <a
href="ioc-cookbook-service-configurations.html">symbol</a> mechanism allows
configuration values to be defined and perhaps overridden, then provided to
services via injection, using the @<a class="external-link"
href="http://tapestry.apache.org
/current/apidocs/org/apache/tapestry5/ioc/annotations/Value.html">Value</a>
annotation.</p><p>The first step is to contribute values.</p><div class="code
panel pdl" style="border-style: solid;border-width: 1px;"><div
class="codeContent panelContent pdl">
+</div></div><p><a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/ChainBuilder.html">ChainBuilder</a>
is a service that <em>builds other services</em>. Here it creates an object of
type Dispatcher in terms of the list of Dispatchers. This is one of the most
common uses of service builder methods ... for when the service implementation
doesn't exist, but can be constructed at runtime.</p><h1
id="IoCcookbook-ServiceConfigurations-MappedConfigurations">Mapped
Configurations</h1><p>The last type of service configuration is the mapped
service configuration. Here we relate a key, often a string, to some value. The
contributions are ultimately combined to form a Map.</p><p>Tapestry IoC's <a
href="symbols.html">symbol</a> mechanism allows configuration values to be
defined and perhaps overridden, then provided to services via injection, using
the @<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/
tapestry5/ioc/annotations/Value.html">Value</a> annotation.</p><p>The first
step is to contribute values.</p><div class="code panel pdl"
style="border-style: solid;border-width: 1px;"><div class="codeContent
panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> public static void
contributeFactoryDefaults(MappedConfiguration<String, String>
configuration)
{
configuration.add(SymbolConstants.FILE_CHECK_INTERVAL, "1000"); // 1 second
Modified: websites/production/tapestry/content/performance-and-clustering.html
==============================================================================
--- websites/production/tapestry/content/performance-and-clustering.html
(original)
+++ websites/production/tapestry/content/performance-and-clustering.html Mon
Feb 19 03:21:26 2018
@@ -109,7 +109,7 @@
</div>
-<h2 id="PerformanceandClustering-PerformanceTips">Performance Tips</h2><p>But
even with all of Tapestry's built-in speediness, to really get top performance
you'll need to be sure you're not hamstringing Tapestry. As a start, use the
following checklist:</p><ul><li>Ensure (be absolutely sure) that <a
href="performance-and-clustering.html">Production Mode</a> is turned on in
production.</li><li>Minimize the use of the HTTPSession (see below), especially
if you're using clustering.</li><li>Set <a
href="performance-and-clustering.html">tapestry.clustered-sessions</a> to
"false" if you aren't using clustering.</li><li>Organize your JavaScript files
into <a
href="performance-and-clustering.html">JavaScriptStacks</a>.</li><li>Ensure
that your static resources (images, CSS, JavaScript) are being cached by the
browser.<ul><li>Use "asset:" or "context:" <a
href="performance-and-clustering.html">binding prefixes</a> for all links to
static resources (images, CSS, JavaScript).</li><li>Make
sure that your firewall, proxy server, load balancer, front-end web servers,
and app servers all allow caching of static resources.</li><li>Ensure
"cache-control" and "vary" HTTP headers are set correctly for your static
resources.</li><li>Use a client-based tool (like Firebug) to examine the
requests that your browser makes as you navigate through the site. You should
<em>not</em> see repeated requests for static
resources.</li></ul></li><li>Consider using a <a class="external-link"
href="http://en.wikipedia.org/wiki/Content_delivery_network"
rel="nofollow">Content Delivery Network</a> for static parts of your
site.</li></ul><p>After all of the above issues are addressed, if you still
have performance problems, they probably aren't related to Tapestry.</p><h2
id="PerformanceandClustering-ClusteringversusStickySessions">Clustering versus
Sticky Sessions</h2><p>For web applications, <strong>clustering</strong> is a
load-balancing technique in which multiple application servers are
set up to behave as one big server. Generally this requires replicating
HttpSession data across the servers, to ensure that a user's web interactions
will continue without interruption regardless of which server handles the next
request. Session replication achieves very high reliability, but it incurs an
extra performance cost (due to the serializing and deserializing of session
data and the extra network traffic required).</p><p>In contrast, <strong>Sticky
Sessions</strong> (also called <em>session persistence</em> or <em>sticky
persistence</em>) is a load balancing technique in which each session is
assigned to a particular server for the duration of the session. This approach
doesn't require copying HTTPSession data between servers, so it's very
scalable. But if a server goes down, all of its sessions are lost.</p><p>In
general, the sticky sessions approach is the way to go when possible (that is,
when performance is more important than session survival). It represents a much
mo
re efficient use of resources ... you are scaling <em>out</em> not
<em>up</em>, which is always cheaper. It also means that you don't have to be
as careful about what goes into the HTTPSession.</p><p><em>For details on
setting up clustering and sticky sessions, see the documentation of whatever
load balancer you are using.</em></p><h2
id="PerformanceandClustering-Clustering">Clustering</h2><p>Tapestry is designed
to be "a good citizen" of an application server that supports clustering. It is
careful about what it writes into the HttpSession. The framework understands
that the server that receives a request may not be the same one that rendered
the page initially; this knowledge affects many code paths, and it guides the
approach Tapestry takes to caching page and component properties.</p><p>Your
part is to properly manage the objects put into the HttpSession (via
@SessionAttribute, @SessionState or @Persist; see <a
href="performance-and-clustering.html">Performance and Clustering</
a>):</p><ul><li>Don't store anything in the session that you don't have to.
Principally this means minimizing the use of @Persist (see <a
href="performance-and-clustering.html">Page Activation</a> and <a
href="performance-and-clustering.html">Performance and Clustering</a>), storing
only IDs in the session rather than whole entities.</li><li>Where possible,
persist only objects that are immutable (i.e., String, or a primitive or
wrapper type).</li><li>Only put <em>serializable</em> objects into the
session.</li><li>Make use of the @ImmutableSessionPersistedObject annotation
and OptimizedSessionPersistedObject interface (both described
below).</li></ul><p>Again, Tapestry is a good citizen, but from the application
server's point of view, it's just another servlet application. The heavy
lifting here is application server specific.</p><p></p><h2
id="PerformanceandClustering-ClusteringIssues">Clustering Issues</h2><p>The
Servlet API was designed with the intention that there would be
only a modest amount of server-side state, and that the stored values would be
individual numbers and strings, and thus, immutable.</p><p>However, many web
applications do not use the HttpSession this way, instead storing large,
mutable objects in the session. This is not a problem for single servers, but
in a cluster, anything stored in the session must be serialized to a bytestream
and distributed to other servers within the cluster, and restored
there.</p><p>Most application servers perform that serialization and
distribution whenever HttpSession.setAttribute() is called. This creates a data
consistency problem for mutable objects, because if you read a mutable session
object, change its state, but <em>don't</em> invoke setAttribute(), the changes
will be isolated to just a single server in the cluster.</p><p>Tapestry
attempts to solve this: any session-persisted object that is read during a
request will be re-stored back into the HttpSession at the end of the request.
This ensur
es that changed internal state of those mutable objects is properly replicated
around the cluster.</p><p>But while this solution solves the data consistency
problem, it does so at the expense of performance, since all of those calls to
setAttribute() result in extra session data being replicated needlessly if the
internal state of the mutable object hasn't changed.</p><p>Tapestry has
solutions to this, too:</p><h3
id="PerformanceandClustering-@ImmutableSessionPersistedObjectAnnotation">@ImmutableSessionPersistedObject
Annotation</h3><p>Tapestry knows that Java's String, Number and Boolean
classes are immutable. Immutable objects do not require a re-store into the
session.</p><p>You can mark your own session objects as immutable (and thus not
requiring session replication) using the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/ImmutableSessionPersistedObject.html">ImmutableSessionPersistedObject</a>
annotation.</p><h3 id=
"PerformanceandClustering-OptimizedSessionPersistedObjectInterface">OptimizedSessionPersistedObject
Interface</h3><p>The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/OptimizedSessionPersistedObject">OptimizedSessionPersistedObject</a>
interface allows an object to control this behavior. An object with this
interface can track when its mutable state changes. Typically, you should
extend from the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.html">BaseOptimizedSessionPersistedObject</a>
base class.</p><h3
id="PerformanceandClustering-SessionPersistedObjectAnalyzerService">SessionPersistedObjectAnalyzer
Service</h3><p>The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.html">SessionPersistedObjectAnalyzer</a>
service is ultimately responsible for determining whethe
r a session persisted object is dirty or not (dirty meaning in need of a
restore into the session). This is an extensible service where new strategies,
for new classes, can be introduced.</p></div>
+<h2 id="PerformanceandClustering-PerformanceTips">Performance Tips</h2><p>But
even with all of Tapestry's built-in speediness, to really get top performance
you'll need to be sure you're not hamstringing Tapestry. As a start, use the
following checklist:</p><ul><li>Ensure (be absolutely sure) that <a
href="configuration.html">Production Mode</a> is turned on in
production.</li><li>Minimize the use of the HTTPSession (see below), especially
if you're using clustering.</li><li>Set <a
href="configuration.html">tapestry.clustered-sessions</a> to "false" if you
aren't using clustering.</li><li>Organize your JavaScript files into <a
href="legacy-javascript.html">JavaScriptStacks</a>.</li><li>Ensure that your
static resources (images, CSS, JavaScript) are being cached by the
browser.<ul><li>Use "asset:" or "context:" <a
href="component-parameters.html">binding prefixes</a></li><li> for all
links to static resources (images, CSS, JavaScript).</li><li>Make sure that
your firewall, p
roxy server, load balancer, front-end web servers, and app servers all allow
caching of static resources.</li><li>Ensure "cache-control" and "vary" HTTP
headers are set correctly for your static resources.</li><li>Use a client-based
tool (like Firebug) to examine the requests that your browser makes as you
navigate through the site. You should <em>not</em> see repeated requests for
static resources.</li></ul></li><li>Consider using a <a class="external-link"
href="http://en.wikipedia.org/wiki/Content_delivery_network"
rel="nofollow">Content Delivery Network</a> for static parts of your
site.</li></ul><p>After all of the above issues are addressed, if you still
have performance problems, they probably aren't related to Tapestry.</p><h2
id="PerformanceandClustering-ClusteringversusStickySessions">Clustering versus
Sticky Sessions</h2><p>For web applications, <strong>clustering</strong> is a
load-balancing technique in which multiple application servers are set up to
behave as one big
server. Generally this requires replicating HttpSession data across the
servers, to ensure that a user's web interactions will continue without
interruption regardless of which server handles the next request. Session
replication achieves very high reliability, but it incurs an extra performance
cost (due to the serializing and deserializing of session data and the extra
network traffic required).</p><p>In contrast, <a class="external-link"
href="https://en.wikipedia.org/wiki/Load_balancing_(computing)#Persistence"
rel="nofollow"><strong>Sticky Sessions</strong></a> (also called <em>session
persistence</em> or <em>sticky persistence</em>) is a load balancing technique
in which each session is assigned to a particular server for the duration of
the session. This approach doesn't require copying HTTPSession data between
servers, so it's very scalable. But if a server goes down, all of its sessions
are lost.</p><p>In general, the sticky sessions approach is the way to go when
possibl
e (that is, when performance is more important than session survival). It
represents a much more efficient use of resources ... you are scaling
<em>out</em> not <em>up</em>, which is always cheaper. It also means that you
don't have to be as careful about what goes into the HTTPSession.</p><p><em>For
details on setting up clustering and sticky sessions, see the documentation of
whatever load balancer you are using.</em></p><h2
id="PerformanceandClustering-Clustering">Clustering</h2><p>Tapestry is designed
to be "a good citizen" of an application server that supports clustering. It is
careful about what it writes into the HttpSession. The framework understands
that the server that receives a request may not be the same one that rendered
the page initially; this knowledge affects many code paths, and it guides the
approach Tapestry takes to caching page and component properties.</p><p>Your
part is to properly manage the objects put into the HttpSession (via
@SessionAttribute, @Session
State or @Persist; see <a href="performance-and-clustering.html">Performance
and Clustering</a>):</p><ul><li>Don't store anything in the session that you
don't have to. Principally this means minimizing the use of @Persist (see <a
href="page-navigation.html">Page Activation</a> and <a
href="using-select-with-a-list.html">Using Select With a List</a>), storing
only IDs in the session rather than whole entities.</li><li>Where possible,
persist only objects that are immutable (i.e., String, or a primitive or
wrapper type).</li><li>Only put <em>serializable</em> objects into the
session.</li><li>Make use of the @ImmutableSessionPersistedObject annotation
and OptimizedSessionPersistedObject interface (both described
below).</li></ul><p>Again, Tapestry is a good citizen, but from the application
server's point of view, it's just another servlet application. The heavy
lifting here is application server specific.</p><p></p><h2
id="PerformanceandClustering-ClusteringIssues">Clustering Iss
ues</h2><p>The Servlet API was designed with the intention that there would be
only a modest amount of server-side state, and that the stored values would be
individual numbers and strings, and thus, immutable.</p><p>However, many web
applications do not use the HttpSession this way, instead storing large,
mutable objects in the session. This is not a problem for single servers, but
in a cluster, anything stored in the session must be serialized to a bytestream
and distributed to other servers within the cluster, and restored
there.</p><p>Most application servers perform that serialization and
distribution whenever HttpSession.setAttribute() is called. This creates a data
consistency problem for mutable objects, because if you read a mutable session
object, change its state, but <em>don't</em> invoke setAttribute(), the changes
will be isolated to just a single server in the cluster.</p><p>Tapestry
attempts to solve this: any session-persisted object that is read during a
request wi
ll be re-stored back into the HttpSession at the end of the request. This
ensures that changed internal state of those mutable objects is properly
replicated around the cluster.</p><p>But while this solution solves the data
consistency problem, it does so at the expense of performance, since all of
those calls to setAttribute() result in extra session data being replicated
needlessly if the internal state of the mutable object hasn't
changed.</p><p>Tapestry has solutions to this, too:</p><h3
id="PerformanceandClustering-@ImmutableSessionPersistedObjectAnnotation">@ImmutableSessionPersistedObject
Annotation</h3><p>Tapestry knows that Java's String, Number and Boolean
classes are immutable. Immutable objects do not require a re-store into the
session.</p><p>You can mark your own session objects as immutable (and thus not
requiring session replication) using the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/ImmutableSessionP
ersistedObject.html">ImmutableSessionPersistedObject</a> annotation.</p><h3
id="PerformanceandClustering-OptimizedSessionPersistedObjectInterface">OptimizedSessionPersistedObject
Interface</h3><p>The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/OptimizedSessionPersistedObject">OptimizedSessionPersistedObject</a>
interface allows an object to control this behavior. An object with this
interface can track when its mutable state changes. Typically, you should
extend from the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.html">BaseOptimizedSessionPersistedObject</a>
base class.</p><h3
id="PerformanceandClustering-SessionPersistedObjectAnalyzerService">SessionPersistedObjectAnalyzer
Service</h3><p>The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.html">SessionPersi
stedObjectAnalyzer</a> service is ultimately responsible for determining
whether a session persisted object is dirty or not (dirty meaning in need of a
restore into the session). This is an extensible service where new strategies,
for new classes, can be introduced.</p></div>
</div>
<div class="clearer"></div>
Modified: websites/production/tapestry/content/using-select-with-a-list.html
==============================================================================
--- websites/production/tapestry/content/using-select-with-a-list.html
(original)
+++ websites/production/tapestry/content/using-select-with-a-list.html Mon Feb
19 03:21:26 2018
@@ -77,10 +77,10 @@
</div>
<div id="content">
- <div id="ConfluenceContent"><h1
id="UsingSelectWithaList-UsingSelectWithaList">Using Select With a
List</h1><p>The documentation for the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Select.html">Select
Component</a> and the <a href="using-select-with-a-list.html">Tapestry
Tutorial</a> provide simplistic examples of populating a drop-down menu (as the
(X)HTML <em>Select</em> element) using comma-delimited strings and enums.
However, most real-world Tapestry applications need to populate such menus
using values from a database, commonly in the form of java.util.List objects.
Doing so generally requires a <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/SelectModel.html">SelectModel</a>
and a <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ValueEncoder.html">ValueEncoder</a>
bound to the Select component wi
th its "model" and "encoder" parameters:</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeContent panelContent pdl">
+ <div id="ConfluenceContent"><h1
id="UsingSelectWithaList-UsingSelectWithaList">Using Select With a
List</h1><p>The documentation for the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Select.html">Select
Component</a> and the <a href="tapestry-tutorial.html">Tapestry Tutorial</a>
provide simplistic examples of populating a drop-down menu (as the (X)HTML
<em>Select</em> element) using comma-delimited strings and enums. However, most
real-world Tapestry applications need to populate such menus using values from
a database, commonly in the form of java.util.List objects. Doing so generally
requires a <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/SelectModel.html">SelectModel</a>
and a <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ValueEncoder.html">ValueEncoder</a>
bound to the Select component with its
"model" and "encoder" parameters:</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"><t:select t:id="colorMenu" value="selectedColor"
model="ColorSelectModel" encoder="colorEncoder" />
</pre>
-</div></div><p>In the above example, ColorSelectModel must be of type
SelectModel, or anything that Tapestry knows how to <a
href="using-select-with-a-list.html">coerce</a> into a SelectModel, such as a
List or a Map or a "value=label,value=label,..." delimited string, or anything
Tapestry knows how to coerce into a List or Map, such as an Array or a
comma-delimited String.</p><h2
id="UsingSelectWithaList-SelectModel">SelectModel</h2><p></p><div
class="navmenu" style="float:right; background:#eee; margin:3px; padding:0 1em">
+</div></div><p>In the above example, ColorSelectModel must be of type
SelectModel, or anything that Tapestry knows how to <a
href="parameter-type-coercion.html">coerce</a> into a SelectModel, such as a
List or a Map or a "value=label,value=label,..." delimited string, or anything
Tapestry knows how to coerce into a List or Map, such as an Array or a
comma-delimited String.</p><h2
id="UsingSelectWithaList-SelectModel">SelectModel</h2><p></p><div
class="navmenu" style="float:right; background:#eee; margin:3px; padding:0 1em">
<p> <strong>JumpStart Demos:</strong><br clear="none">
<a class="external-link"
href="http://jumpstart.doublenegative.com.au/jumpstart/examples/select/totalcontrolobject"
rel="nofollow">Total Control Object Select</a><br clear="none">
<a class="external-link"
href="http://jumpstart.doublenegative.com.au/jumpstart/examples/select/id"
rel="nofollow">ID Select</a><br clear="none">
@@ -107,7 +107,7 @@ public String toString() {
</pre>
</div></div><p>But that is contorting the purpose of the toString() method,
and if you go to that much trouble you're already half way to the recommended
practice: creating a ValueEncoder.</p><h2
id="UsingSelectWithaList-ValueEncoder">ValueEncoder</h2><p>In addition to a
SelectModel, your Select menu is likely to need a ValueEncoder. While a
SelectModel is concerned only with how to construct a Select menu, a
ValueEncoder is used when constructing the Select menu <em>and</em> when
interpreting the encoded value that is submitted back to the server. A
ValueEncoder is a converter between the type of objects you want to represent
as options in the menu and the client-side encoded values that uniquely
identify them, and vice-versa.</p><div class="navmenu" style="float:right;
background:#eee; margin:3px; padding:0 1em">
<p> <strong>JumpStart Demo:</strong><br clear="none">
- <a class="external-link"
href="http://jumpstart.doublenegative.com.au/jumpstart/examples/select/easyobject"
rel="nofollow">Easy Object Select</a></p></div>Most commonly, your
ValueEncoder's toClient() method will return a unique ID (e.g. a database
primary key, or perhaps a UUID) of the given object, and its toValue() method
will return the <em>object</em> matching the given ID by doing a database
lookup (ideally using a service or DAO method).<p>If you're using one of the
ORM integration modules (<a
href="using-select-with-a-list.html">Tapestry-Hibernate</a>, <a
href="using-select-with-a-list.html">Tapestry-JPA</a>, or <a
class="external-link"
href="http://code.google.com/p/tapestry5-cayenne/wiki/ValueEncoder"
rel="nofollow">Tapestry-Cayenne</a>), the ValueEncoder is automatically
provided for each of your mapped entity classes. The Hibernate module's
implementation is typical: the primary key field of the object (converted to a
String) is used as the client-side value, and
that same primary key is used to look up the selected object.</p><p>That's
exactly what you should do in your own ValueEncoders too:</p><div class="code
panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>ColorEncoder.java (perhaps in your
com.example.myappname.encoders package)</b></div><div class="codeContent
panelContent pdl">
+ <a class="external-link"
href="http://jumpstart.doublenegative.com.au/jumpstart/examples/select/easyobject"
rel="nofollow">Easy Object Select</a></p></div>Most commonly, your
ValueEncoder's toClient() method will return a unique ID (e.g. a database
primary key, or perhaps a UUID) of the given object, and its toValue() method
will return the <em>object</em> matching the given ID by doing a database
lookup (ideally using a service or DAO method).<p>If you're using one of the
ORM integration modules (<a href="hibernate.html">Tapestry-Hibernate</a>, <a
href="integrating-with-jpa.html">Tapestry-JPA</a>, or <a class="external-link"
href="http://code.google.com/p/tapestry5-cayenne/wiki/ValueEncoder"
rel="nofollow">Tapestry-Cayenne</a>), the ValueEncoder is automatically
provided for each of your mapped entity classes. The Hibernate module's
implementation is typical: the primary key field of the object (converted to a
String) is used as the client-side value, and that same primary
key is used to look up the selected object.</p><p>That's exactly what you
should do in your own ValueEncoders too:</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>ColorEncoder.java (perhaps in your
com.example.myappname.encoders package)</b></div><div class="codeContent
panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public class ColorEncoder implements
ValueEncoder<Color>, ValueEncoderFactory<Color> {
@Inject
@@ -169,7 +169,7 @@ public String toString() {
configuration.addInstance(SomeOtherType.class,
SomeOtherTypeEncoder.class);
}
</pre>
-</div></div><h2 id="UsingSelectWithaList-WhatifIomittheValueEncoder?">What if
I omit the ValueEncoder?</h2><p>The Select component's "encoder" parameter is
optional, but if the "value" parameter is bound to a complex object (not a
simple String, Integer, etc.) and you don't provide a ValueEncoder with the
"encoder" parameter (and one isn't provided automatically by, for example, the
Tapestry Hibernate integration), you'll receive a "Could not find a coercion"
exception (when you submit the form) as Tapestry tries to convert the selected
option's encoded value back to the <em>object</em> in your Select's "value"
parameter. To fix this, you'll either have to 1) provide a ValueEncoder, 2)
provide a <a href="using-select-with-a-list.html">Coercion</a>, or 3) use a
simple value (String, Integer, etc.) for your Select's "value" parameter, and
then you'll have to add logic in the corresponding onSuccess event listener
method:</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>SelectWithListDemo.tml (partial)</b></div><div class="codeContent
panelContent pdl">
+</div></div><h2 id="UsingSelectWithaList-WhatifIomittheValueEncoder?">What if
I omit the ValueEncoder?</h2><p>The Select component's "encoder" parameter is
optional, but if the "value" parameter is bound to a complex object (not a
simple String, Integer, etc.) and you don't provide a ValueEncoder with the
"encoder" parameter (and one isn't provided automatically by, for example, the
Tapestry Hibernate integration), you'll receive a "Could not find a coercion"
exception (when you submit the form) as Tapestry tries to convert the selected
option's encoded value back to the <em>object</em> in your Select's "value"
parameter. To fix this, you'll either have to 1) provide a ValueEncoder, 2)
provide a <a href="type-coercion.html">Coercion</a>, or 3) use a simple value
(String, Integer, etc.) for your Select's "value" parameter, and then you'll
have to add logic in the corresponding onSuccess event listener method:</p><div
class="code panel pdl" style="border-width: 1px;"><div class="code
Header panelHeader pdl" style="border-bottom-width:
1px;"><b>SelectWithListDemo.tml (partial)</b></div><div class="codeContent
panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"><t:select t:id="colorMenu" value="selectedColorId"
model="ColorSelectModel" />
</pre>
</div></div><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>SelectWithListDemo.java (partial)</b></div><div class="codeContent
panelContent pdl">
@@ -180,7 +180,7 @@ public String toString() {
...
}
</pre>
-</div></div><p>But then again, you may as well create a ValueEncoder
instead.</p><h2 id="UsingSelectWithaList-Whyisthissohard?">Why is this so
hard?</h2><p>Actually, it's really pretty easy if you follow the examples
above. But why is Tapestry designed to use SelectModels and ValueEncoders
anyway? Well, in short, this design allows you to avoid storing (via @Persist,
@SessionAttribute or @SessionState) the entire (potentially large) list of
objects in the session or rebuilding the whole list of objects again (though
only one is needed) when the form is submitted. The chief benefits are reduced
memory use and <a href="using-select-with-a-list.html">more scalable
clustering</a> due to having far less HTTP session data to replicate across the
nodes of a cluster.</p></div>
+</div></div><p>But then again, you may as well create a ValueEncoder
instead.</p><h2 id="UsingSelectWithaList-Whyisthissohard?">Why is this so
hard?</h2><p>Actually, it's really pretty easy if you follow the examples
above. But why is Tapestry designed to use SelectModels and ValueEncoders
anyway? Well, in short, this design allows you to avoid storing (via @Persist,
@SessionAttribute or @SessionState) the entire (potentially large) list of
objects in the session or rebuilding the whole list of objects again (though
only one is needed) when the form is submitted. The chief benefits are reduced
memory use and <a href="performance-and-clustering.html">more scalable
clustering</a> due to having far less HTTP session data to replicate across the
nodes of a cluster.</p></div>
</div>
<div class="clearer"></div>