Modified: websites/production/tapestry/content/persistent-page-data.html
==============================================================================
--- websites/production/tapestry/content/persistent-page-data.html (original)
+++ websites/production/tapestry/content/persistent-page-data.html Sat Feb 3
16:21:22 2018
@@ -27,6 +27,16 @@
</title>
<link type="text/css" rel="stylesheet" href="/resources/space.css" />
+ <link href='/resources/highlighter/styles/shCoreCXF.css'
rel='stylesheet' type='text/css' />
+ <link href='/resources/highlighter/styles/shThemeCXF.css' rel='stylesheet'
type='text/css' />
+ <script src='/resources/highlighter/scripts/shCore.js'
type='text/javascript'></script>
+ <script src='/resources/highlighter/scripts/shBrushJava.js'
type='text/javascript'></script>
+ <script src='/resources/highlighter/scripts/shBrushXml.js'
type='text/javascript'></script>
+ <script src='/resources/highlighter/scripts/shBrushPlain.js'
type='text/javascript'></script>
+ <script>
+ SyntaxHighlighter.defaults['toolbar'] = false;
+ SyntaxHighlighter.all();
+ </script>
<link href="/styles/style.css" rel="stylesheet" type="text/css"/>
@@ -67,23 +77,80 @@
</div>
<div id="content">
- <div id="ConfluenceContent"><rich-text-body><p>The use of the
term "persistence" here refers to <em>page-level</em> persistence, NOT database
persistence.</p></rich-text-body><p>Most instance variables in Tapestry are
automatically cleared at the end of each request. This is important, as it
pertains to how Tapestry pages are shared, over time, by many
users.</p><parameter ac:name="style">float:right</parameter><parameter
ac:name="title">Related Articles</parameter><parameter
ac:name="class">aui-label</parameter><rich-text-body><parameter
ac:name="showLabels">false</parameter><parameter
ac:name="showSpace">false</parameter><parameter ac:name="title">Related
Articles</parameter><parameter ac:name="cql">label = "persistence" and space =
currentSpace()</parameter></rich-text-body><p>However, you often want to store
some data on a <em>single</em> page, and have access to it in later requests to
that same page, without having to store it in a database between requests. (T
o store values across multiple pages, see <a
href="session-storage.html">Session Storage</a>.)</p><p>Making page data
persist across requests to a single page is accomplished with the @<a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Persist.html">Persist</a>
annotation. This annotation is applied to private instance fields of
components:</p><parameter ac:name="">java</parameter><plain-text-body> @Persist
+ <div id="ConfluenceContent"><div
class="confluence-information-macro
confluence-information-macro-information"><span class="aui-icon aui-icon-small
aui-iconfont-info confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>The use of the term "persistence"
here refers to <em>page-level</em> persistence, NOT database
persistence.</p></div></div><p>Most instance variables in Tapestry are
automatically cleared at the end of each request. This is important, as it
pertains to how Tapestry pages are shared, over time, by many users.</p><div
class="aui-label" style="float:right" title="Related Articles">
+
+
+
+
+
+
+
+
+<h3>Related Articles</h3>
+
+<ul class="content-by-label"><li>
+ <div>
+ <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
+
+ <div class="details">
+ <a href="performance-and-clustering.html">Performance
and Clustering</a>
+
+
+ </div>
+ </li><li>
+ <div>
+ <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
+
+ <div class="details">
+ <a href="session-storage.html">Session Storage</a>
+
+
+ </div>
+ </li><li>
+ <div>
+ <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
+
+ <div class="details">
+ <a href="persistent-page-data.html">Persistent Page
Data</a>
+
+
+ </div>
+ </li></ul>
+</div>
+
+
+<p>However, you often want to store some data on a <em>single</em> page, and
have access to it in later requests to that same page, without having to store
it in a database between requests. (To store values across multiple pages, see
<a href="persistent-page-data.html">Persistent Page Data</a>.)</p><p>Making
page data persist across requests to a single page is accomplished with the @<a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Persist.html">Persist</a>
annotation. This annotation is applied to private instance fields of
components:</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;"> @Persist
private int value;
-</plain-text-body><p>Such annotated fields will retain their state between
requests. Generally, speaking, this means that the value is stored into the
session (but other approaches are possible).</p><p>Whenever you make a change
to a persistent field, its value is saved. On later requests to the same page,
the value for the field is restored.</p><h2
id="PersistentPageData-PersistenceStrategies">Persistence Strategies</h2><p>The
value for each field is the <em>strategy</em> used to store the field between
requests.</p><h3 id="PersistentPageData-SessionStrategy">Session
Strategy</h3><p><plain-text-body>{float:right|background=#eee|padding=0 1em}
- *JumpStart Demo:*
- [Storing Data in a
Page|http://jumpstart.doublenegative.com.au/jumpstart/examples/state/storingdatainapage]
- [Passing Data Between
Pages|http://jumpstart.doublenegative.com.au/jumpstart/examples/state/passingdatabetweenpages]
-{float}</plain-text-body>The session strategy stores field changes into the
session; the session is created as necessary. Session strategy is the default
strategy used unless otherwise overridden.</p><p>A suitably long session
attribute name is used; it incorporates the name of the page, the nested
component id, and the name of the field.</p><parameter ac:name="title">Example:
Session Strategy</parameter><plain-text-body> @Persist
+</pre>
+</div></div><p>Such annotated fields will retain their state between requests.
Generally, speaking, this means that the value is stored into the session (but
other approaches are possible).</p><p>Whenever you make a change to a
persistent field, its value is saved. On later requests to the same page, the
value for the field is restored.</p><h2
id="PersistentPageData-PersistenceStrategies">Persistence Strategies</h2><p>The
value for each field is the <em>strategy</em> used to store the field between
requests.</p><h3 id="PersistentPageData-SessionStrategy">Session
Strategy</h3><p></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/state/storingdatainapage"
rel="nofollow">Storing Data in a Page</a><br clear="none">
+ <a class="external-link"
href="http://jumpstart.doublenegative.com.au/jumpstart/examples/state/passingdatabetweenpages"
rel="nofollow">Passing Data Between Pages</a></p></div>The session strategy
stores field changes into the session; the session is created as necessary.
Session strategy is the default strategy used unless otherwise overridden.<p>A
suitably long session attribute name is used; it incorporates the name of the
page, the nested component id, and the name of the field.</p><div class="code
panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>Example: Session Strategy</b></div><div
class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist
private int value;
-</plain-text-body><h3 id="PersistentPageData-FlashStrategy">Flash
Strategy</h3><p>The flash strategy stores information in the session as well,
just for not very long. Values are stored into the session, but then deleted
from the session as they are first used to restore a page's state.</p><p>The
flash is typically used to store temporary messages that should only be
displayed to the user once.</p><parameter ac:name="title">Example: Flash
Strategy</parameter><plain-text-body> @Persist(PersistenceConstants.FLASH)
+</pre>
+</div></div><h3 id="PersistentPageData-FlashStrategy">Flash
Strategy</h3><p>The flash strategy stores information in the session as well,
just for not very long. Values are stored into the session, but then deleted
from the session as they are first used to restore a page's state.</p><p>The
flash is typically used to store temporary messages that should only be
displayed to the user once.</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>Example: Flash Strategy</b></div><div class="codeContent panelContent
pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist(PersistenceConstants.FLASH)
private int value;
-</plain-text-body><h3 id="PersistentPageData-ClientStrategy">Client
Strategy</h3><p>The field is persisted onto the client; you will see an
additional query parameter in each URL (or an extra hidden field in each
form).</p><p>Client persistence is somewhat expensive. It can bloat the size of
the rendered pages by adding hundreds of characters to each link. There is
extra processing on each request to de-serialize the values encoded into the
query parameter.</p><p>Client persistence does not scale very well; as more
information is stored into the query parameter, its length can become
problematic. In many cases, web browsers, firewalls or other servers may
silently truncate the URL which will break the application.</p><p>Use client
persistence with care, and store a minimal amount of data. Try to store the
identity (that is, primary key) of an object, rather than the object
itself.</p><parameter ac:name="title">Example: Client
Strategy</parameter><plain-text-body> @Persist(Persisten
ceConstants.CLIENT)
+</pre>
+</div></div><h3 id="PersistentPageData-ClientStrategy">Client
Strategy</h3><p>The field is persisted onto the client; you will see an
additional query parameter in each URL (or an extra hidden field in each
form).</p><p>Client persistence is somewhat expensive. It can bloat the size of
the rendered pages by adding hundreds of characters to each link. There is
extra processing on each request to de-serialize the values encoded into the
query parameter.</p><p>Client persistence does not scale very well; as more
information is stored into the query parameter, its length can become
problematic. In many cases, web browsers, firewalls or other servers may
silently truncate the URL which will break the application.</p><p>Use client
persistence with care, and store a minimal amount of data. Try to store the
identity (that is, primary key) of an object, rather than the object
itself.</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="bord
er-bottom-width: 1px;"><b>Example: Client Strategy</b></div><div
class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist(PersistenceConstants.CLIENT)
private int value;
-</plain-text-body><h3
id="PersistentPageData-HibernateEntityStrategy">Hibernate Entity
Strategy</h3><p><span style="line-height: 1.4285715;">Entity persistence is
provided by the tapestry-hibernate module (which extends Tapestry with new
features).</span></p><p>In Entity persistence, the field should store a
Hibernate entity instance.</p><parameter ac:name="title">"Hibernate Entity
Strategy"</parameter><plain-text-body>
@Persist(HibernatePersistenceConstants.ENTITY)
- private User user;</plain-text-body><p> </p><p>The value stored in the
HttpSession is a <em>token</em> for the entity: its Java class name and
primary key. When the field is restored in a later request, the entity is
re-instantiated using that data.</p><p>What is <em>not</em> stored is any
changes to the persistent entity that are not committed to the external
datastore (the database).</p><p>Starting in Tapestry 5.4, it is possible to
store a non-persistent entity (a transient entity). A transient entity is
stored directly into the HttpSession, and should be Serializable if the
application is clustered.</p><h3 id="PersistentPageData-JPAEntityStrategy">JPA
Entity Strategy</h3><p>The tapestry-jpa module uses a similar strategy.
However, at the current time it can only store a persisted entity (one that has
been saved to the database and has a primary key).</p><parameter
ac:name="title">"Example: JPA Entity Strategy"</parameter><plain-text-body>
@Persist(JpaPersistenc
eConstants.ENTITY)
- private Account account;</plain-text-body><p><span style="color:
rgb(83,145,38);font-size: 20.0px;line-height: 1.5;">Persistence Strategy
Inheritance</span></p><p>By default the value for the Persist annotation is the
empty string. When this is true, then the actual strategy to be used is
determined by a search up the component hierarchy.</p><p>For each component,
the meta-data property <code>tapestry.persistence-strategy</code> is checked.
This can be specified using the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Meta.html">Meta</a>
annotation.</p><p>If the value is non-blank, then that strategy is used. This
allows a component to control the persistence strategy used inside any
sub-components (that don't explicitly use a different strategy).</p><p>In any
case, if no component provides the meta data, then the ultimate default,
"session", is used.</p><h2 id="PersistentPageData-DefaultValues">Default
Values</h2><p>Fie
lds marked with @Persist may not have default values (whether set inline, or
inside a constructor).</p><h2
id="PersistentPageData-ClearingPersistentFields">Clearing Persistent
Fields</h2><p>If you reach a point where you know that all data for a page can
be discarded, you can do exactly that.</p><p>The method
<code>discardPersistentFieldChanges()</code> of ComponentResources will discard
all persistent fields for the page, regardless of which strategy is used to
store the property. This will not affect the page in memory, but takes effect
for subsequent requests.</p><p><parameter ac:name=""><a
href="clustering-issues.html">Clustering Issues</a></parameter></p><parameter
ac:name="title">Example: Entity Session Strategy</parameter><plain-text-body>
@Persist(HibernatePersistenceConstants.ENTITY)
- private User user;</plain-text-body><parameter ac:name="title">"Example:JAP
Session Strategy"</parameter><plain-text-body>
@Persist(JpaPersistenceConstants.ENTITY)
- private Account account;</plain-text-body></div>
+</pre>
+</div></div><h3 id="PersistentPageData-HibernateEntityStrategy">Hibernate
Entity Strategy</h3><p><span>Entity persistence is provided by the
tapestry-hibernate module (which extends Tapestry with new
features).</span></p><p>In Entity persistence, the field should store a
Hibernate entity instance.</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>"Hibernate Entity Strategy"</b></div><div class="codeContent
panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist(HibernatePersistenceConstants.ENTITY)
+ private User user;</pre>
+</div></div><p> </p><p>The value stored in the HttpSession is
a <em>token</em> for the entity: its Java class name and primary key. When
the field is restored in a later request, the entity is re-instantiated using
that data.</p><p>What is <em>not</em> stored is any changes to the
persistent entity that are not committed to the external datastore (the
database).</p><p>Starting in Tapestry 5.4, it is possible to store a
non-persistent entity (a transient entity). A transient entity is stored
directly into the HttpSession, and should be Serializable if the application is
clustered.</p><h3 id="PersistentPageData-JPAEntityStrategy">JPA Entity
Strategy</h3><p>The tapestry-jpa module uses a similar strategy. However, at
the current time it can only store a persisted entity (one that has been saved
to the database and has a primary key).</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>"Exa
mple: JPA Entity Strategy"</b></div><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist(JpaPersistenceConstants.ENTITY)
+ private Account account;</pre>
+</div></div><p><span style="color: rgb(83,145,38);">Persistence Strategy
Inheritance</span></p><p>By default the value for the Persist annotation is the
empty string. When this is true, then the actual strategy to be used is
determined by a search up the component hierarchy.</p><p>For each component,
the meta-data property <code>tapestry.persistence-strategy</code> is checked.
This can be specified using the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Meta.html">Meta</a>
annotation.</p><p>If the value is non-blank, then that strategy is used. This
allows a component to control the persistence strategy used inside any
sub-components (that don't explicitly use a different strategy).</p><p>In any
case, if no component provides the meta data, then the ultimate default,
"session", is used.</p><h2 id="PersistentPageData-DefaultValues">Default
Values</h2><p>Fields marked with @Persist may not have default values (whether
set i
nline, or inside a constructor).</p><h2
id="PersistentPageData-ClearingPersistentFields">Clearing Persistent
Fields</h2><p>If you reach a point where you know that all data for a page can
be discarded, you can do exactly that.</p><p>The method
<code>discardPersistentFieldChanges()</code> of ComponentResources will discard
all persistent fields for the page, regardless of which strategy is used to
store the property. This will not affect the page in memory, but takes effect
for subsequent requests.</p><p></p><h2
id="PersistentPageData-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 se
ssion 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 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 o
bject hasn't changed.</p><p>Tapestry has solutions to this, too:</p><h3
id="PersistentPageData-@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="PersistentPageData-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="PersistentPageData-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 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 class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>Example: Entity Session Strategy</b></div
><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist(HibernatePersistenceConstants.ENTITY)
+ private User user;</pre>
+</div></div><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>"Example:JAP Session Strategy"</b></div><div class="codeContent
panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Persist(JpaPersistenceConstants.ENTITY)
+ private Account account;</pre>
+</div></div></div>
</div>
<div class="clearer"></div>
Modified: websites/production/tapestry/content/property-expressions.html
==============================================================================
--- websites/production/tapestry/content/property-expressions.html (original)
+++ websites/production/tapestry/content/property-expressions.html Sat Feb 3
16:21:22 2018
@@ -67,7 +67,7 @@
</div>
<div id="content">
- <div id="ConfluenceContent"><p>Tapestry uses <strong>property
expressions</strong> to move data between components. Property expressions are
the basis of the <a href="component-parameters.html">component parameters</a>
and <a href="component-templates.html">template expansions</a>.</p><div
class="aui-label" style="float:right" title="Related Articles">
+ <div id="ConfluenceContent"><p>Tapestry uses <strong>property
expressions</strong> to move data between components. Property expressions are
the basis of the <a href="property-expressions.html">component parameters</a>
and <a href="property-expressions.html">template expansions</a>.</p><div
class="aui-label" style="float:right" title="Related Articles">
@@ -100,7 +100,7 @@
</div>
-<p>A property expression is a string that explains to Tapestry how to navigate
from a root object (the containing component) through some number of
properties, to a final property that can be read or updated. In fact, that's a
simplification, as the properties may actually be public fields, or even method
invocations.</p><p>As elsewhere in Tapestry, the names of fields, properties,
and methods are recognized in a case-insensitive manner.</p><p>The most basic
form of a property expression is a simple property name, such as
"userName".</p><p>A series of property names may be specified, separated by
periods: "user.name", which is equivalent to either
<code>getUser().getName()</code> or <code>getUser().setName()</code>, depending
on context (whether the expression is being read or updated).</p><p>The "." is
called the "dereference operator". A second operator is "?." or the "safe
dereference operator". This works the same as "." except that it allows any of
the properties to be null. Wh
en reading, a null short-circuits the expression (which returns null). When
writing to a property, an intermediate null value will cause the value to be
discarded without error.</p><p>In other words, "user?.name" works, even when
the <code>user</code> property may be null.</p><p>Property expressions can also
reference public methods. Methods may take parameters (or not), but must not
return void. When the final term in a property expression is a method, then the
property expression is read-only.</p><p>Being able to invoke methods was
originally added so that it was possible to access methods such as
<code>java.util.Map.size()</code> (which is not named like a property getter
method). In Tapestry 5.1, property expressions were extended so that parameters
could be passed into methods.</p><p>Parameters to methods are, themselves,
property expressions. This means that you can write a property expression that
reads a property and passes it as a parameter to a method, and then access a pr
operty of the object returned from the method.</p><h2
id="PropertyExpressions-Compilation">Compilation</h2><p>Property expressions
are compiled to Java classes at runtime; there is no runtime reflection (unlike
the OGNL expression language used in prior releases of Tapestry).</p><h2
id="PropertyExpressions-Grammar">Grammar</h2><p>Expressed in simplified <a
class="external-link"
href="http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form"
rel="nofollow">Backus–Naur Form</a>, the grammar of property expressions
is as follows:</p><div class="preformatted panel" style="border-width:
1px;"><div class="preformattedContent panelContent">
+<p>A property expression is a string that explains to Tapestry how to navigate
from a root object (the containing component) through some number of
properties, to a final property that can be read or updated. In fact, that's a
simplification, as the properties may actually be public fields, or even method
invocations.</p><p>As elsewhere in Tapestry, the names of fields, properties,
and methods are recognized in a case-insensitive manner.</p><p>The most basic
form of a property expression is a simple property name, such as
"userName".</p><p>A series of property names may be specified, separated by
periods: "user.name", which is equivalent to either
<code>getUser().getName()</code> or <code>getUser().setName()</code>, depending
on context (whether the expression is being read or updated).</p><p>The "<span
style="color: rgb(255,0,0);"><strong>.</strong></span>" is called the
"dereference operator". In addition, there is a a "safe dereference operator",
"<span style="color: rgb(255,0,0)
;">?.</span>", which  works the same as "<span style="color:
rgb(255,0,0);">.</span>" except that it allows any of the properties to be
null. When reading, a null short-circuits the expression (which returns null).
When writing to a property, an intermediate null value will cause the value to
be discarded without error.</p><p>In other words, "user<span style="color:
rgb(255,0,0);">?.</span>name" works, even when the <code>user</code> property
may be null.</p><p>Property expressions can also reference public methods.
Methods may take parameters (or not), but must not return void. When the final
term in a property expression is a method, then the property expression is
read-only.</p><p>Being able to invoke methods was originally added so that it
was possible to access methods such as <code>java.util.Map.size()</code> (which
is not named like a property getter method). In Tapestry 5.1, property
expressions were extended so that parameters could be passed into
methods.</p><p>Parame
ters to methods are, themselves, property expressions. This means that you can
write a property expression that reads a property and passes it as a parameter
to a method, and then access a property of the object returned from the
method.</p><h2 id="PropertyExpressions-Compilation">Compilation</h2><p>Property
expressions are compiled to Java classes at runtime; there is no runtime
reflection (unlike the OGNL expression language used in prior releases of
Tapestry).</p><h2 id="PropertyExpressions-Grammar">Grammar</h2><p>Expressed in
simplified <a class="external-link"
href="http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form"
rel="nofollow">Backus–Naur Form</a>, the grammar of property expressions
is as follows:</p><div class="preformatted panel" style="border-width:
1px;"><div class="preformattedContent panelContent">
<pre>expression : keyword | rangeOp | constant | propertyChain | list | notOp
| map;
keyword : 'null' | 'this';
@@ -139,7 +139,7 @@ mapKey : keyword | constant | propertyCh
</div></div>
<div class="error"><span class="error">Unknown macro: {div}</span>
<p>Support for map literals was added in Tapestry 5.3.</p>
-</div>Notes:<ul><li>Whitespace is ignored.</li><li>Integers and decimals may
have a leading sign ('+' or '-').</li><li>Constants are in base 10 (octal and
hex notation is not yet supported). Decimals may contain a decimal point
(exponent notation not yet supported).</li><li>Literal strings are enclosed in
single quotes.</li><li>The <code>rangeOp</code> creates a range object that
will iterate between the two values. The upper and lower bounds may be literal
integers, or property expressions.</li><li>An identifier by itself is a
property name. An identifier with parenthesis is a method
invocation.</li><li>Property names, method names, and keywords are
case-insensitive.</li><li>'this' is the root object (i.e., the containing
component).</li><li>The <code>not</code> operator coerces the expression to a
<code>boolean</code> (so it can be used on strings, numbers,
etc.).</li><li>Method matching is based on method name and number of
parameters, but not parameter types. The <a href="type-
coercion.html">TypeCoercer</a> service is used to convert parameters to the
correct type to be passed into the method.</li></ul><h2
id="PropertyExpressions-Examples">Examples</h2><div class="table-wrap"><table
class="confluenceTable"><tbody><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p> </p></th><th colspan="1" rowspan="1"
class="confluenceTh"><p>Example</p></th><th colspan="1" rowspan="1"
class="confluenceTh"><p>Notes</p></th></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Keyword</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>this</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p> </p></td></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Keyword</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>null</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p> </p></td></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Property Name</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><
p>userName</p></td><td colspan="1" rowspan="1" class="confluenceTd"><p>Calls
getUserName() or setUserName, depending on context</p></td></tr><tr><th
colspan="1" rowspan="1" class="confluenceTh"><p>Property Chain</p></th><td
colspan="1" rowspan="1" class="confluenceTd"><p>user.address.city</p></td><td
colspan="1" rowspan="1" class="confluenceTd"><p>Calls
getUser().getAddress().getCity() or getUser().getAddress().setCity(), depending
on context</p></td></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Property Chain</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>user?.name</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p>Calls getUser() and, if the result is not null, calls
getName() on the result</p></td></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Method Invocation</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>groupList.size()</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p>calls getGroupList().size()</p></
td></tr><tr><th colspan="1" rowspan="1" class="confluenceTh"><p>Method
Invocation</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>members.findById(user.id)?.name</p></td><td colspan="1"
rowspan="1" class="confluenceTd"><p>Calls
getMembers().findById(getUser().getId())?.getName() (unless findById returns
null)</p></td></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Range</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>1..10</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p>Iterates between integers 1 and 10</p></td></tr><tr><th
colspan="1" rowspan="1" class="confluenceTh"><p>Range</p></th><td colspan="1"
rowspan="1" class="confluenceTd"><p>1..groupList.size()</p></td><td colspan="1"
rowspan="1" class="confluenceTd"><p>Iterates between 1 and the result of
getGroupList().size()</p></td></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Literal String</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>'Beer is proof that
God loves us and wants us to be happy.'</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p>Use single quotes</p></td></tr><tr><th colspan="1"
rowspan="1" class="confluenceTh"><p>List</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>[user.name, user.email, user.phone]</p></td><td
colspan="1" rowspan="1" class="confluenceTd"><p> </p></td></tr><tr><th
colspan="1" rowspan="1" class="confluenceTh"><p>Not Operator</p></th><td
colspan="1" rowspan="1" class="confluenceTd"><p>! user.deleted</p></td><td
colspan="1" rowspan="1" class="confluenceTd"><p>the boolean negation of
getUser().getDeleted()</p></td></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Not, Coerced</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>! user.middleName</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p>true only if getUser.getMiddleName() returns null or an
empty string</p></td></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Map</p></th><td cols
pan="1" rowspan="1" class="confluenceTd"><p>{ 'framework' : 'Tapestry',
'version' : version }</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p>Keys are string literals (in single quotes), but could
be properties as well</p></td></tr></tbody></table></div></div>
+</div>Notes:<ul><li>Whitespace is ignored.</li><li>Integers and decimals may
have a leading sign ('+' or '-').</li><li>Constants are in base 10 (octal and
hex notation is not yet supported). Decimals may contain a decimal point
(exponent notation not yet supported).</li><li>Literal strings are enclosed in
single quotes.</li><li>The <code>rangeOp</code> creates a range object that
will iterate between the two values. The upper and lower bounds may be literal
integers, or property expressions.</li><li>An identifier by itself is a
property name. An identifier with parenthesis is a method
invocation.</li><li>Property names, method names, and keywords are
case-insensitive.</li><li>'this' is the root object (i.e., the containing
component).</li><li>The <code>not</code> operator coerces the expression to a
<code>boolean</code> (so it can be used on strings, numbers,
etc.).</li><li>Method matching is based on method name and number of
parameters, but not parameter types. The <a href="prope
rty-expressions.html">TypeCoercer</a> service is used to convert parameters to
the correct type to be passed into the method.</li></ul><h2
id="PropertyExpressions-Examples">Examples</h2><div class="table-wrap"><table
class="confluenceTable"><tbody><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p> </p></th><th colspan="1" rowspan="1"
class="confluenceTh"><p>Example</p></th><th colspan="1" rowspan="1"
class="confluenceTh"><p>Notes</p></th></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Keyword</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>this</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p> </p></td></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Keyword</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>null</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p> </p></td></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Property Name</p></th><td colspan="1" rowspan="1"
class="confluen
ceTd"><p>userName</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p>Calls getUserName() or setUserName, depending on
context</p></td></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Property Chain</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>user.address.city</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p>Calls getUser().getAddress().getCity() or
getUser().getAddress().setCity(), depending on context</p></td></tr><tr><th
colspan="1" rowspan="1" class="confluenceTh"><p>Property Chain</p></th><td
colspan="1" rowspan="1" class="confluenceTd"><p>user?.name</p></td><td
colspan="1" rowspan="1" class="confluenceTd"><p>Calls getUser() and, if the
result is not null, calls getName() on the result</p></td></tr><tr><th
colspan="1" rowspan="1" class="confluenceTh"><p>Method Invocation</p></th><td
colspan="1" rowspan="1" class="confluenceTd"><p>groupList.size()</p></td><td
colspan="1" rowspan="1" class="confluenceTd"><p>calls getGroupList().size(
)</p></td></tr><tr><th colspan="1" rowspan="1" class="confluenceTh"><p>Method
Invocation</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>members.findById(<a class="external-link"
href="http://user.id" rel="nofollow">user.id</a>)?.name</p></td><td colspan="1"
rowspan="1" class="confluenceTd"><p>Calls
getMembers().findById(getUser().getId())?.getName() (unless findById returns
null)</p></td></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Range</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>1..10</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p>Iterates between integers 1 and 10</p></td></tr><tr><th
colspan="1" rowspan="1" class="confluenceTh"><p>Range</p></th><td colspan="1"
rowspan="1" class="confluenceTd"><p>1..groupList.size()</p></td><td colspan="1"
rowspan="1" class="confluenceTd"><p>Iterates between 1 and the result of
getGroupList().size()</p></td></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Literal String</p></t
h><td colspan="1" rowspan="1" class="confluenceTd"><p>'Beer is proof that God
loves us and wants us to be happy.'</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p>Use single quotes</p></td></tr><tr><th colspan="1"
rowspan="1" class="confluenceTh"><p>List</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>[<a class="external-link" href="http://user.name"
rel="nofollow">user.name</a>, user.email, user.phone]</p></td><td colspan="1"
rowspan="1" class="confluenceTd"><p> </p></td></tr><tr><th colspan="1"
rowspan="1" class="confluenceTh"><p>Not Operator</p></th><td colspan="1"
rowspan="1" class="confluenceTd"><p>! user.deleted</p></td><td colspan="1"
rowspan="1" class="confluenceTd"><p>the boolean negation of
getUser().getDeleted()</p></td></tr><tr><th colspan="1" rowspan="1"
class="confluenceTh"><p>Not, Coerced</p></th><td colspan="1" rowspan="1"
class="confluenceTd"><p>! user.middleName</p></td><td colspan="1" rowspan="1"
class="confluenceTd"><p>true only if ge
tUser.getMiddleName() returns null or an empty string</p></td></tr><tr><th
colspan="1" rowspan="1" class="confluenceTh"><p>Map</p></th><td colspan="1"
rowspan="1" class="confluenceTd"><p>{ 'framework' : 'Tapestry', 'version' :
version }</p></td><td colspan="1" rowspan="1" class="confluenceTd"><p>Keys are
string literals (in single quotes), but could be properties as
well</p></td></tr></tbody></table></div></div>
</div>
<div class="clearer"></div>
Modified: websites/production/tapestry/content/request-processing.html
==============================================================================
--- websites/production/tapestry/content/request-processing.html (original)
+++ websites/production/tapestry/content/request-processing.html Sat Feb 3
16:21:22 2018
@@ -36,13 +36,26 @@
<div class="wrapper bs">
- <div id="navigation"><div class="nav"><ul class="alternate"><li><a
href="index.html">Home</a></li><li><a href="getting-started.html">Getting
Started</a></li><li><a href="documentation.html">Documentation</a></li><li><a
href="download.html">Download</a></li><li><a
href="about.html">About</a></li><li><a class="external-link"
href="http://www.apache.org/licenses/LICENSE-2.0">License</a></li><li><a
href="community.html">Community</a></li><li><a class="external-link"
href="http://www.apache.org/security/">Security</a></li><li><a
class="external-link" href="http://www.apache.org/">Apache</a></li><li><a
class="external-link"
href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li><li><a
class="external-link"
href="http://www.apache.org/foundation/thanks.html">Thanks</a></li></ul></div></div>
+ <div id="navigation"><div class="nav"><ul class="alternate"><li><a
href="index.html">Home</a></li><li><a href="getting-started.html">Getting
Started</a></li><li><a href="documentation.html">Documentation</a></li><li><a
href="download.html">Download</a></li><li><a
href="about.html">About</a></li><li><a class="external-link"
href="http://www.apache.org/licenses/LICENSE-2.0">License</a></li><li><a
href="community.html">Community</a></li><li><a class="external-link"
href="http://www.apache.org/security/">Security</a></li><li><a
class="external-link" href="http://www.apache.org/">Apache</a></li><li><a
class="external-link"
href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li><li><a
class="external-link"
href="http://www.apache.org/foundation/thanks.html">Thanks</a></li></ul></div>
+
+</div>
<div id="top">
- <div id="smallbanner"><div class="searchbox"
style="float:right;margin: .3em 1em .1em 1em"><span style="color: #999;
font-size: 90%">Tapestry docs, issues, wikis & blogs:</span><form
enctype="application/x-www-form-urlencoded" method="get"
action="http://tapestry.apache.org/search.html">
- <input type="text" name="q">
- <input type="submit" value="Search">
-</form></div><div class="emblem" style="float:left"><p><a
href="index.html"><span class="confluence-embedded-file-wrapper"><img
class="confluence-embedded-image confluence-external-resource"
src="http://tapestry.apache.org/images/tapestry_small.png"
data-image-src="http://tapestry.apache.org/images/tapestry_small.png"></span></a></p></div><div
class="title" style="float:left; margin: 0 0 0 3em"><h1
id="SmallBanner-PageTitle">Request Processing</h1></div></div>
+ <div id="smallbanner"><div class="searchbox"
style="float:right;margin: .3em 1em .1em 1em"><span style="color: #999;
font-size: 90%">Tapestry docs, issues, wikis & blogs:</span>
+<form enctype="application/x-www-form-urlencoded" method="get"
action="http://tapestry.apache.org/search.html">
+ <input type="text" name="q">
+ <input type="submit" value="Search">
+</form>
+
+</div>
+
+
+<div class="emblem" style="float:left"><p><a href="index.html"><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource"
src="http://tapestry.apache.org/images/tapestry_small.png"
data-image-src="http://tapestry.apache.org/images/tapestry_small.png"></span></a></p></div>
+
+
+<div class="title" style="float:left; margin: 0 0 0 3em"><h1
id="SmallBanner-PageTitle">Request Processing</h1></div>
+
+</div>
<div class="clearer"></div>
</div>
@@ -54,43 +67,76 @@
</div>
<div id="content">
- <div id="ConfluenceContent"><div class="aui-label"
style="float:right" title="Related Articles"><h3>Related Articles</h3><ul
class="content-by-label"><li>
- <div>
- <span class="icon aui-icon aui-icon-small aui-iconfont-page-default"
title="Page">Page:</span>
- </div>
- <div class="details">
- <a href="page-navigation.html">Page Navigation</a>
- </div> </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small aui-iconfont-page-default"
title="Page">Page:</span>
- </div>
- <div class="details">
- <a href="page-life-cycle.html">Page Life Cycle</a>
- </div> </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small aui-iconfont-page-default"
title="Page">Page:</span>
- </div>
- <div class="details">
- <a href="component-rendering.html">Component Rendering</a>
- </div> </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small aui-iconfont-page-default"
title="Page">Page:</span>
- </div>
- <div class="details">
- <a href="component-events.html">Component Events</a>
- </div> </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small aui-iconfont-page-default"
title="Page">Page:</span>
- </div>
- <div class="details">
- <a href="component-events-faq.html">Component Events FAQ</a>
- </div> </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small aui-iconfont-page-default"
title="Page">Page:</span>
- </div>
- <div class="details">
- <a href="request-processing.html">Request Processing</a>
- </div> </li></ul></div><p><strong>Request Processing</strong> involves a
sequence of steps that Tapestry performs when every HTTP request comes in. You
<em>don't need</em> to know these steps to use Tapestry productively, but
understanding the request processing pipeline is helpful if you want to
understand Tapestry deeply.</p><p>Much of the early stages of processing are in
the form of extensible <a
href="pipelinebuilder-service.html">pipelines</a>.</p><h2
id="RequestProcessing-TapestryFilter">Tapestry Filter</h2><p>All incoming
requests originate with the TapestryFilter, which is a servlet filter
configured inside your application's <a
href="configuration.html">web.xml</a>.</p><p>The TapestryFilter is responsible
for a number of startup and initialization functions.</p><p>When it receives a
request, the TapestryFilter obtains the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/HttpServletRequestHandler.html">HttpServletR
equestHandler</a> service, and invokes its service() method.</p><h2
id="RequestProcessing-HttpServletRequestHandlerPipeline">HttpServletRequestHandler
Pipeline</h2><p>This pipeline performs initial processing of the request. It
can be extended by contributing a <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/HttpServletRequestFilter.html">HttpServletRequestFilter</a>
into the HttpServletRequestHandler service's configuration.</p><p>Tapestry
does not contribute any filters into this pipeline of its own.</p><p>The
terminator for the pipeline does two things:</p><ul><li>It stores the request
and response into the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/RequestGlobals.html">RequestGlobals</a>
service. This is a per-thread scoped service that stores
per-thread/per-request information.</li><li>It wraps the request and response
as a <a class="external-link" href="http:
//tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/Request.html">Request</a>
and <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/Response.html">Response</a>,
and passes them into the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/RequestHandler.html">RequestHandler</a>
pipeline.</li></ul><h2
id="RequestProcessing-RequestHandlerPipeline">RequestHandler
Pipeline</h2><p>This pipeline is where most extensions related to requests take
place. Request represents an abstraction on top of HttpServletRequest.
(Primarily, this exists to bridge from the Servlet API objects to the
corresponding Tapestry objects. This is to allow for a possible portlet
integration for Tapestry.) Where other code and services within Tapestry
require access to information in the request, such as query parameters, that
information is obtained from the Request (or Response) objects.</p><
p>The RequestHandler pipeline includes a number of built-in
filters:</p><ul><li>CheckForUpdates is responsible for <a
href="class-reloading.html">class and template
reloading</a>.</li><li>Localization identifies the <a
href="localization.html">locale for the user</a>.</li><li>StaticFiles checks
for URLs that are for static files (files that exist inside the web context)
and aborts the request, so that the servlet container can handle the request
normally.</li><li>ErrorFilter catches uncaught exceptions from the lower levels
of Tapestry and presents the exception report page. This involves the <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/RequestExceptionHandler.html">RequestExceptionHandler</a>
service, which is responsible for initializing and rendering the <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/pages/ExceptionReport.html">core/ExceptionReport</a>
page.</li>
</ul><p>The terminator for this pipeline stores the Request and the Response
into RequestGlobals, then requests that the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/Dispatcher.html">MasterDispatcher</a>
service figure out how to handle the request (if it is, indeed, a Tapestry
request).</p><h2 id="RequestProcessing-MasterDispatcherService">Master
Dispatcher Service</h2><p>The MasterDispatcher service is a chain-of-command,
aggregating together (in a specific order), several Dispatcher objects. Each
Dispatcher is built to recognize and process a particular kind of URL.</p><h3
id="RequestProcessing-RootPathDispatcher">RootPath Dispatcher</h3><p>The
RootPath Dispatcher recognizes a request for the application root (i.e., "/")
and handles this the same as a render request for the "Start" page. Support for
the Start page is kept for legacy purposes. Index pages are the correct
approach.</p><h3 id="RequestProcessing-AssetDispatc
her">Asset Dispatcher</h3><p>Requests that begin with "/assets/" are
references to <a href="assets.html">asset resources</a> that are stored on the
classpath, inside the Tapestry JARs (or perhaps inside the JAR for a component
library). The contents of the file will be delivered to the client browser as a
byte stream. This dispatcher also handles requests that are simply polling for
a change to the file.</p><h3
id="RequestProcessing-PageRenderDispatcher">PageRender Dispatcher</h3><p>Page
render requests are requests to render a particular page. Such requests may
include additional elements on the path, which will be treated as activation
context (see ComponentEvent Dispatcher below). Generally speaking, the
activation context is the primary key of some related entity object. This
allows the page to reconstruct the state it will need to successfully render
itself.</p><p>The event handler method for the activate event may return a
value; this is treated the same as the return value f
rom a component action request; typically this will result in a redirect to
another page. In this way, the activate event can perform simple validation at
the page level ("can the user see this page?").</p><p>Page render URLs consist
of the logical name of the page plus additional path elements for the
activation context. The dispatcher here strips terms off of the path until it
finds a known page name. Thus, "/mypage/27" would look first for a page whose
name was "mypage/27", then look for a page name "mypage". Assuming the second
search was successful, the page would be activated with the context "27". If no
logical page name can be identified, control passes to the next
dispatcher.</p><h3
id="RequestProcessing-ComponentEventDispatcher">ComponentEvent
Dispatcher</h3><p>The ComponentEvent dispatcher is used to trigger events in
components.</p><p>The URL identifies the name of the page, then a series of
component ids (the path from the page down to the specific component), then the
name of the event to be triggered on the component. The remaining path
elements are used as the context for the <em>event</em> (not for the page
activation, which does not currently apply). For example, "/griddemo.FOO.BAR/3"
would locate page "griddemo", then component "FOO.BAR", and trigger an event
named "action" (the default event type, which is omitted from the URL), with
the context "3".</p><p>If the page in question has an activation context, it is
supplied as an additional query parameter on the link.</p><p>In cases where the
event type is not the default, "action", it will appear between the nested
component id and the event context, preceded by a colon. Example:
"/example/foo.bar:magic/99" would trigger an event of type "magic". This is not
common in the vanilla Tapestry framework, but will likely be more common as
Ajax features (which would not use the normal request logic) are
implemented.</p><p>The response from a component action request is typically,
but not universall
y, used to send a redirect to the client; the redirect URL is a page render
URL to display the response to the event. This is detailed under <a
href="page-navigation.html">Page Navigation</a>.</p><h2
id="RequestProcessing-RequestGlobalsService">RequestGlobals Service</h2><p>The
RequestGlobals service has a life cycle of per-thread; this means that a
separate instance exists for every thread, and therefore, for every request.
The terminators of the two handler pipelines store the request/response pairs
into the RequestGlobals service.</p><h2
id="RequestProcessing-RequestService">Request Service</h2><p>The Request
service is a <a href="shadowbuilder-service.html">shadow</a> of the
RequestGlobals services' request property. That is, any methods invoked on this
service are delegated to the request object stored inside the
RequestGlobals.</p><h2 id="RequestProcessing-Overview">Overview</h2><p>The
following diagram provides an overview of how the different pipelines, filters
and dispatc
hers interact when processing an incoming request.</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-content-image-border"
src="request-processing.data/tapestry_request_processing_800.png"></span></p></div>
+ <div id="ConfluenceContent"><div class="aui-label"
style="float:right" title="Related Articles">
+
+
+
+
+
+
+
+
+<h3>Related Articles</h3>
+
+<ul class="content-by-label"><li>
+ <div>
+ <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
+
+ <div class="details">
+ <a href="page-navigation.html">Page Navigation</a>
+
+
+ </div>
+ </li><li>
+ <div>
+ <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
+
+ <div class="details">
+ <a href="page-life-cycle.html">Page Life Cycle</a>
+
+
+ </div>
+ </li><li>
+ <div>
+ <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
+
+ <div class="details">
+ <a href="component-rendering.html">Component
Rendering</a>
+
+
+ </div>
+ </li><li>
+ <div>
+ <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
+
+ <div class="details">
+ <a href="component-events.html">Component Events</a>
+
+
+ </div>
+ </li><li>
+ <div>
+ <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
+
+ <div class="details">
+ <a href="component-events-faq.html">Component Events
FAQ</a>
+
+
+ </div>
+ </li><li>
+ <div>
+ <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
+
+ <div class="details">
+ <a href="request-processing.html">Request
Processing</a>
+
+
+ </div>
+ </li></ul>
+</div>
+
+
+<p><strong>Request Processing</strong> involves a sequence of steps that
Tapestry performs when every HTTP request comes in. You <em>don't need</em> to
know these steps to use Tapestry productively, but understanding the request
processing pipeline is helpful if you want to understand Tapestry
deeply.</p><p>Much of the early stages of processing are in the form of
extensible <a href="request-processing.html">pipelines</a>.</p><h2
id="RequestProcessing-TapestryFilter">Tapestry Filter</h2><p>All incoming
requests originate with the TapestryFilter, which is a servlet filter
configured inside your application's <a
href="request-processing.html">web.xml</a>.</p><p>The TapestryFilter is
responsible for a number of startup and initialization functions.</p><p>When it
receives a request, the TapestryFilter obtains the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/HttpServletRequestHandler.html">HttpServletRequestHandler</a>
service
, and invokes its service() method.</p><h2
id="RequestProcessing-HttpServletRequestHandlerPipeline">HttpServletRequestHandler
Pipeline</h2><p>This pipeline performs initial processing of the request. It
can be extended by contributing a <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/HttpServletRequestFilter.html">HttpServletRequestFilter</a>
into the HttpServletRequestHandler service's configuration.</p><p>Tapestry
does not contribute any filters into this pipeline of its own.</p><p>The
terminator for the pipeline does two things:</p><ul><li>It stores the request
and response into the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/RequestGlobals.html">RequestGlobals</a>
service. This is a per-thread scoped service that stores
per-thread/per-request information.</li><li>It wraps the request and response
as a <a class="external-link" href="http://tapestry.apache.org/cur
rent/apidocs/org/apache/tapestry5/services/Request.html">Request</a> and <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/Response.html">Response</a>,
and passes them into the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/RequestHandler.html">RequestHandler</a>
pipeline.</li></ul><h2
id="RequestProcessing-RequestHandlerPipeline">RequestHandler
Pipeline</h2><p>This pipeline is where most extensions related to requests take
place. Request represents an abstraction on top of HttpServletRequest.
(Primarily, this exists to bridge from the Servlet API objects to the
corresponding Tapestry objects. This is to allow for a possible portlet
integration for Tapestry.) Where other code and services within Tapestry
require access to information in the request, such as query parameters, that
information is obtained from the Request (or Response) objects.</p><p>The
RequestHandler pipe
line includes a number of built-in filters:</p><ul><li>CheckForUpdates is
responsible for <a href="request-processing.html">class and template
reloading</a>.</li><li>Localization identifies the <a
href="request-processing.html">locale for the user</a>.</li><li>StaticFiles
checks for URLs that are for static files (files that exist inside the web
context) and aborts the request, so that the servlet container can handle the
request normally.</li><li>ErrorFilter catches uncaught exceptions from the
lower levels of Tapestry and presents the exception report page. This involves
the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/RequestExceptionHandler.html">RequestExceptionHandler</a>
service, which is responsible for initializing and rendering the <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/pages/ExceptionReport.html">core/ExceptionReport</a>
page.</li></ul><p>The term
inator for this pipeline stores the Request and the Response into
RequestGlobals, then requests that the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/Dispatcher.html">MasterDispatcher</a>
service figure out how to handle the request (if it is, indeed, a Tapestry
request).</p><h2 id="RequestProcessing-MasterDispatcherService">Master
Dispatcher Service</h2><p>The MasterDispatcher service is a chain-of-command,
aggregating together (in a specific order), several Dispatcher objects. Each
Dispatcher is built to recognize and process a particular kind of URL.</p><h3
id="RequestProcessing-RootPathDispatcher">RootPath Dispatcher</h3><p>The
RootPath Dispatcher recognizes a request for the application root (i.e., "/")
and handles this the same as a render request for the "Start" page. Support for
the Start page is kept for legacy purposes. Index pages are the correct
approach.</p><h3 id="RequestProcessing-AssetDispatcher">Asset Dispa
tcher</h3><p>Requests that begin with "/assets/" are references to <a
href="request-processing.html">asset resources</a> that are stored on the
classpath, inside the Tapestry JARs (or perhaps inside the JAR for a component
library). The contents of the file will be delivered to the client browser as a
byte stream. This dispatcher also handles requests that are simply polling for
a change to the file.</p><h3
id="RequestProcessing-PageRenderDispatcher">PageRender Dispatcher</h3><p>Page
render requests are requests to render a particular page. Such requests may
include additional elements on the path, which will be treated as activation
context (see ComponentEvent Dispatcher below). Generally speaking, the
activation context is the primary key of some related entity object. This
allows the page to reconstruct the state it will need to successfully render
itself.</p><p>The event handler method for the activate event may return a
value; this is treated the same as the return value from
a component action request; typically this will result in a redirect to
another page. In this way, the activate event can perform simple validation at
the page level ("can the user see this page?").</p><p>Page render URLs consist
of the logical name of the page plus additional path elements for the
activation context. The dispatcher here strips terms off of the path until it
finds a known page name. Thus, "/mypage/27" would look first for a page whose
name was "mypage/27", then look for a page name "mypage". Assuming the second
search was successful, the page would be activated with the context "27". If no
logical page name can be identified, control passes to the next
dispatcher.</p><h3
id="RequestProcessing-ComponentEventDispatcher">ComponentEvent
Dispatcher</h3><p>The ComponentEvent dispatcher is used to trigger events in
components.</p><p>The URL identifies the name of the page, then a series of
component ids (the path from the page down to the specific component), then the
name
of the event to be triggered on the component. The remaining path elements
are used as the context for the <em>event</em> (not for the page activation,
which does not currently apply). For example, "/griddemo.FOO.BAR/3" would
locate page "griddemo", then component "FOO.BAR", and trigger an event named
"action" (the default event type, which is omitted from the URL), with the
context "3".</p><p>If the page in question has an activation context, it is
supplied as an additional query parameter on the link.</p><p>In cases where the
event type is not the default, "action", it will appear between the nested
component id and the event context, preceded by a colon. Example:
"/example/foo.bar:magic/99" would trigger an event of type "magic". This is not
common in the vanilla Tapestry framework, but will likely be more common as
Ajax features (which would not use the normal request logic) are
implemented.</p><p>The response from a component action request is typically,
but not universally, u
sed to send a redirect to the client; the redirect URL is a page render URL to
display the response to the event. This is detailed under <a
href="request-processing.html">Request Processing</a>.</p><h2
id="RequestProcessing-RequestGlobalsService">RequestGlobals Service</h2><p>The
RequestGlobals service has a life cycle of per-thread; this means that a
separate instance exists for every thread, and therefore, for every request.
The terminators of the two handler pipelines store the request/response pairs
into the RequestGlobals service.</p><h2
id="RequestProcessing-RequestService">Request Service</h2><p>The Request
service is a <a href="request-processing.html">shadow</a> of the
RequestGlobals services' request property. That is, any methods invoked on this
service are delegated to the request object stored inside the
RequestGlobals.</p><h2 id="RequestProcessing-Overview">Overview</h2><p>The
following diagram provides an overview of how the different pipelines, filters
and dispatch
ers interact when processing an incoming request.</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource confluence-content-image-border"
src="https://cwiki-test.apache.org/confluence/download/attachments/22872133/tapestry_request_processing_800.png?version=1&modificationDate=1299128361000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/22872133/tapestry_request_processing_800.png?version=1&modificationDate=1299128361000&api=v2"></span></p></div>
</div>
<div class="clearer"></div>
Modified: websites/production/tapestry/content/response-compression.html
==============================================================================
--- websites/production/tapestry/content/response-compression.html (original)
+++ websites/production/tapestry/content/response-compression.html Sat Feb 3
16:21:22 2018
@@ -36,13 +36,26 @@
<div class="wrapper bs">
- <div id="navigation"><div class="nav"><ul class="alternate"><li><a
href="index.html">Home</a></li><li><a href="getting-started.html">Getting
Started</a></li><li><a href="documentation.html">Documentation</a></li><li><a
href="download.html">Download</a></li><li><a
href="about.html">About</a></li><li><a class="external-link"
href="http://www.apache.org/licenses/LICENSE-2.0">License</a></li><li><a
href="community.html">Community</a></li><li><a class="external-link"
href="http://www.apache.org/security/">Security</a></li><li><a
class="external-link" href="http://www.apache.org/">Apache</a></li><li><a
class="external-link"
href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li><li><a
class="external-link"
href="http://www.apache.org/foundation/thanks.html">Thanks</a></li></ul></div></div>
+ <div id="navigation"><div class="nav"><ul class="alternate"><li><a
href="index.html">Home</a></li><li><a href="getting-started.html">Getting
Started</a></li><li><a href="documentation.html">Documentation</a></li><li><a
href="download.html">Download</a></li><li><a
href="about.html">About</a></li><li><a class="external-link"
href="http://www.apache.org/licenses/LICENSE-2.0">License</a></li><li><a
href="community.html">Community</a></li><li><a class="external-link"
href="http://www.apache.org/security/">Security</a></li><li><a
class="external-link" href="http://www.apache.org/">Apache</a></li><li><a
class="external-link"
href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li><li><a
class="external-link"
href="http://www.apache.org/foundation/thanks.html">Thanks</a></li></ul></div>
+
+</div>
<div id="top">
- <div id="smallbanner"><div class="searchbox"
style="float:right;margin: .3em 1em .1em 1em"><span style="color: #999;
font-size: 90%">Tapestry docs, issues, wikis & blogs:</span><form
enctype="application/x-www-form-urlencoded" method="get"
action="http://tapestry.apache.org/search.html">
- <input type="text" name="q">
- <input type="submit" value="Search">
-</form></div><div class="emblem" style="float:left"><p><a
href="index.html"><span class="confluence-embedded-file-wrapper"><img
class="confluence-embedded-image confluence-external-resource"
src="http://tapestry.apache.org/images/tapestry_small.png"
data-image-src="http://tapestry.apache.org/images/tapestry_small.png"></span></a></p></div><div
class="title" style="float:left; margin: 0 0 0 3em"><h1
id="SmallBanner-PageTitle">Response Compression</h1></div></div>
+ <div id="smallbanner"><div class="searchbox"
style="float:right;margin: .3em 1em .1em 1em"><span style="color: #999;
font-size: 90%">Tapestry docs, issues, wikis & blogs:</span>
+<form enctype="application/x-www-form-urlencoded" method="get"
action="http://tapestry.apache.org/search.html">
+ <input type="text" name="q">
+ <input type="submit" value="Search">
+</form>
+
+</div>
+
+
+<div class="emblem" style="float:left"><p><a href="index.html"><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource"
src="http://tapestry.apache.org/images/tapestry_small.png"
data-image-src="http://tapestry.apache.org/images/tapestry_small.png"></span></a></p></div>
+
+
+<div class="title" style="float:left; margin: 0 0 0 3em"><h1
id="SmallBanner-PageTitle">Response Compression</h1></div>
+
+</div>
<div class="clearer"></div>
</div>
@@ -54,25 +67,49 @@
</div>
<div id="content">
- <div id="ConfluenceContent"><p>Starting in Tapestry 5.1, the
framework automatically GZIP <strong>compresses</strong> content streamed to
the client. This can significantly reduce the amount of network traffic for a
Tapestry application, at the cost of extra processing time on the server to
compress the response stream.</p><div class="aui-label" style="float:right"
title="Related Articles"><h3>Related Articles</h3><ul
class="content-by-label"><li>
- <div>
- <span class="icon aui-icon aui-icon-small aui-iconfont-page-default"
title="Page">Page:</span>
- </div>
- <div class="details">
- <a href="request-processing.html">Request Processing</a>
- </div> </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small aui-iconfont-page-default"
title="Page">Page:</span>
- </div>
- <div class="details">
- <a href="configuration.html">Configuration</a>
- </div> </li><li>
- <div>
- <span class="icon aui-icon aui-icon-small aui-iconfont-page-default"
title="Page">Page:</span>
- </div>
- <div class="details">
- <a href="assets.html">Assets</a>
- </div> </li></ul></div><p>This directly applies to both rendered pages and
streamed assets from the classpath.</p><p>Context assets will also be
compressed ... but this requires referencing such assets using the "context:"
binding prefix, so that generated URL is handled by Tapestry and not the
servlet container.</p><h1
id="ResponseCompression-CompressionConfiguration">Compression
Configuration</h1><p>Main Article: <a
href="configuration.html">Configuration</a></p><p>Small streams generally do
not benefit from being compressed; there is overhead when using compression,
not just the CPU time to compress the bytes, but a lot of overhead. For small
responses, Tapestry does not attempt to compress the output stream.</p><p>The
configuration symbol <code>tapestry.min-gzip-size</code> allows the cutoff to
be set; it defaults to 100 bytes.</p><p>In addition, some file types are
already compressed and should not be recompressed (they actually get larger,
not smaller!). The service <a cla
ss="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/ResponseCompressionAnalyzer.html">ResponseCompressionAnalyzer</a>'s
configuration is an unordered collection of content type strings that should
<em>not</em> be compressed. The default content types are "image/jpeg".</p><h1
id="ResponseCompression-StreamResponse">StreamResponse</h1><p>When returning a
<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/StreamResponse.html">StreamResponse</a>
from a <a href="page-navigation.html">component event method</a>, the stream
is totally under your control; it will not be compressed. You should use the
ResponseCompressionAnalyzer service to determine if the client supports
compression, and add a java.util.zip.GZIPOutputStream to your stream stack if
compression is desired.</p></div>
+ <div id="ConfluenceContent"><p>Starting in Tapestry 5.1, the
framework automatically GZIP <strong>compresses</strong> content streamed to
the client. This can significantly reduce the amount of network traffic for a
Tapestry application, at the cost of extra processing time on the server to
compress the response stream.</p><div class="aui-label" style="float:right"
title="Related Articles">
+
+
+
+
+
+
+
+
+<h3>Related Articles</h3>
+
+<ul class="content-by-label"><li>
+ <div>
+ <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
+
+ <div class="details">
+ <a href="request-processing.html">Request
Processing</a>
+
+
+ </div>
+ </li><li>
+ <div>
+ <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
+
+ <div class="details">
+ <a href="configuration.html">Configuration</a>
+
+
+ </div>
+ </li><li>
+ <div>
+ <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
+
+ <div class="details">
+ <a href="assets.html">Assets</a>
+
+
+ </div>
+ </li></ul>
+</div>
+
+
+<p>This directly applies to both rendered pages and streamed assets from the
classpath.</p><p>Context assets will also be compressed ... but this requires
referencing such assets using the "context:" binding prefix, so that generated
URL is handled by Tapestry and not the servlet container.</p><h1
id="ResponseCompression-CompressionConfiguration">Compression
Configuration</h1><p>Main Article: <a
href="response-compression.html">Response Compression</a></p><p>Small streams
generally do not benefit from being compressed; there is overhead when using
compression, not just the CPU time to compress the bytes, but a lot of
overhead. For small responses, Tapestry does not attempt to compress the output
stream.</p><p>The configuration symbol <code>tapestry.min-gzip-size</code>
allows the cutoff to be set; it defaults to 100 bytes.</p><p>In addition, some
file types are already compressed and should not be recompressed (they actually
get larger, not smaller!). The service <a class="externa
l-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/ResponseCompressionAnalyzer.html">ResponseCompressionAnalyzer</a>'s
configuration is an unordered collection of content type strings that should
<em>not</em> be compressed. The default content types are "image/jpeg".</p><h1
id="ResponseCompression-StreamResponse">StreamResponse</h1><p>When returning a
<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/StreamResponse.html">StreamResponse</a>
from a <a href="response-compression.html">component event method</a>, the
stream is totally under your control; it will not be compressed. You should use
the ResponseCompressionAnalyzer service to determine if the client supports
compression, and add a java.util.zip.GZIPOutputStream to your stream stack if
compression is desired.</p></div>
</div>
<div class="clearer"></div>