Modified:
websites/production/tapestry/content/meta-programming-page-content.html
==============================================================================
--- websites/production/tapestry/content/meta-programming-page-content.html
(original)
+++ websites/production/tapestry/content/meta-programming-page-content.html Sat
Feb 3 18:21:36 2018
@@ -27,6 +27,17 @@
</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/shBrushJScript.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,7 +78,8 @@
</div>
<div id="content">
- <div
id="ConfluenceContent"><p><plain-text-body>{scrollbar}</plain-text-body></p><parameter
ac:name="hidden">true</parameter><parameter
ac:name="atlassian-macro-output-type">BLOCK</parameter><rich-text-body><p>Adding
an Annotation and a Filter to customize Tapestry's page
rendering</p></rich-text-body><h1
id="Meta-ProgrammingPageContent-Meta-ProgrammingPageContent">Meta-Programming
Page Content</h1><p>It is likely that you have some cross-cutting concerns
across your pages, specific features you would like to "mix in" to your pages
without getting tied into knots by inheritance. This is one of those areas
where Tapestry shines.</p><p>This specific example is adapted from a real
client requirement: the client was concerned about other sites wrapping his
content in a frameset and making the site content appear to be theirs. Not all
pages (in some cases, that would be an advantage) but specific pages in the
application. For those pages, the following behaviors were requ
ired:</p><ul><li>Set the X-Frame-Options response header to
"DENY"</li><li>Include JavaScript to "pop" the page out of a frame, if in
one</li></ul><p>Again, this <em>could</em> be done by having a specific
base-class that included a <code>beginRender()</code> method, but the
meta-programming approach is nearly as easy and much more flexible.</p><h2
id="Meta-ProgrammingPageContent-ComponentMeta-Data">Component
Meta-Data</h2><p>In Tapestry, every component (and remember, pages are
components) has <em>meta data</em>: an extra set of key/value pairs stored in
the component's <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ComponentResources.html">ComponentResources</a>.</p><p>By
hooking into the component class transformation pipeline, we can change an
annotation into meta-data that can be accessed by a filter.</p><h2
id="Meta-ProgrammingPageContent-DefiningtheAnnotation">Defining the
Annotation</h2><parameter ac:name="language">java</para
meter><parameter
ac:name="title">ForbidFraming.java</parameter><plain-text-body>package
com.fnord.annotations;
+ <div id="ConfluenceContent"><h1
id="Meta-ProgrammingPageContent-Meta-ProgrammingPageContent">Meta-Programming
Page Content</h1><p>It is likely that you have some cross-cutting concerns
across your pages, specific features you would like to "mix in" to your pages
without getting tied into knots by inheritance. This is one of those areas
where Tapestry shines.</p><p>This specific example is adapted from a real
client requirement: the client was concerned about other sites wrapping his
content in a frameset and making the site content appear to be theirs. Not all
pages (in some cases, that would be an advantage) but specific pages in the
application. For those pages, the following behaviors were
required:</p><ul><li>Set the X-Frame-Options response header to
"DENY"</li><li>Include JavaScript to "pop" the page out of a frame, if in
one</li></ul><p>Again, this <em>could</em> be done by having a specific
base-class that included a <code>beginRender()</code> method, but the
meta-programming approach is nearly as easy and much more flexible.</p><h2
id="Meta-ProgrammingPageContent-ComponentMeta-Data">Component
Meta-Data</h2><p>In Tapestry, every component (and remember, pages are
components) has <em>meta data</em>: an extra set of key/value pairs stored in
the component's <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ComponentResources.html">ComponentResources</a>.</p><p>By
hooking into the component class transformation pipeline, we can change an
annotation into meta-data that can be accessed by a filter.</p><h2
id="Meta-ProgrammingPageContent-DefiningtheAnnotation">Defining the
Annotation</h2><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>ForbidFraming.java</b></div><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">package com.fnord.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
@@ -86,7 +98,9 @@ import java.lang.annotation.Target;
public @interface ForbidFraming {
}
-</plain-text-body><p>This annotation presence is all that's needed; there
aren't any additional attributes to configure it.</p><h2
id="Meta-ProgrammingPageContent-ConvertingtheAnnotationintoMeta-Data">Converting
the Annotation into Meta-Data</h2><p>This is in three parts:</p><ul><li>Define
the meta-data key, and define a constant for that key</li><li>Set a default
meta-data value for the key</li><li>Set a different value for the key when the
annotation is present</li></ul><p>Our key is just "forbid-framing", with values
"true" and "false". The default is "false".</p><h3
id="Meta-ProgrammingPageContent-DefiningtheConstant">Defining the
Constant</h3><parameter ac:name="language">java</parameter><parameter
ac:name="title">FnordSymbols.java</parameter><plain-text-body>package com.fnord;
+</pre>
+</div></div><p>This annotation presence is all that's needed; there aren't any
additional attributes to configure it.</p><h2
id="Meta-ProgrammingPageContent-ConvertingtheAnnotationintoMeta-Data">Converting
the Annotation into Meta-Data</h2><p>This is in three parts:</p><ul><li>Define
the meta-data key, and define a constant for that key</li><li>Set a default
meta-data value for the key</li><li>Set a different value for the key when the
annotation is present</li></ul><p>Our key is just "forbid-framing", with values
"true" and "false". The default is "false".</p><h3
id="Meta-ProgrammingPageContent-DefiningtheConstant">Defining the
Constant</h3><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>FnordSymbols.java</b></div><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">package com.fnord;
import org.apache.tapestry5.services.BaseURLSource;
@@ -104,7 +118,9 @@ public class FnordSymbols {
public static final String FORBID_FRAMING = "forbid-framing";
}
-</plain-text-body><h3
id="Meta-ProgrammingPageContent-SettingtheMeta-DataDefault">Setting the
Meta-Data Default</h3><p>Next, we'll create a module just for the logic
directly related to framing. In the module, we'll define the default value for
the meta-data.</p><parameter ac:name="language">java</parameter><parameter
ac:name="title">ForbidFramingModule.class</parameter><plain-text-body>package
com.fnord.services.forbidframing;
+</pre>
+</div></div><h3
id="Meta-ProgrammingPageContent-SettingtheMeta-DataDefault">Setting the
Meta-Data Default</h3><p>Next, we'll create a module just for the logic
directly related to framing. In the module, we'll define the default value for
the meta-data.</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>ForbidFramingModule.class</b></div><div class="codeContent
panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">package com.fnord.services.forbidframing;
import org.apache.tapestry5.ioc.MappedConfiguration;
import org.apache.tapestry5.ioc.annotations.Contribute;
@@ -122,20 +138,26 @@ public class ForbidFramingModule {
configuration.add(FnordSymbols.FORBID_FRAMING, "false");
}
}
-</plain-text-body><h3
id="Meta-ProgrammingPageContent-MappingtheAnnotation">Mapping the
Annotation</h3><p>Most of the work has already been done for us: we just have
to make a contribution to the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/meta/MetaWorker.html">MetaWorker</a>
service, which is already plugged into the component class transformation
pipeline. MetaWorker spots the annotations we define and uses a second object,
a <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/meta/MetaDataExtractor.html">MetaDataExtractor</a>
we provide, to convert the annotation into a meta-data value.</p><parameter
ac:name="language">java</parameter><parameter
ac:name="lang">java</parameter><parameter
ac:name="title">ForbidFramingModule.java (partial)</parameter><plain-text-body>
@Contribute(MetaWorker.class)
+</pre>
+</div></div><h3 id="Meta-ProgrammingPageContent-MappingtheAnnotation">Mapping
the Annotation</h3><p>Most of the work has already been done for us: we just
have to make a contribution to the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/meta/MetaWorker.html">MetaWorker</a>
service, which is already plugged into the component class transformation
pipeline. MetaWorker spots the annotations we define and uses a second object,
a <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/meta/MetaDataExtractor.html">MetaDataExtractor</a>
we provide, to convert the annotation into a meta-data value.</p><div
class="code panel pdl" style="border-width: 1px;"><div class="codeHeader
panelHeader pdl" style="border-bottom-width: 1px;"><b>ForbidFramingModule.java
(partial)</b></div><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Contribute(MetaWorker.class)
public static void mapAnnotationsToMetaDataValue(
MappedConfiguration<Class, MetaDataExtractor> configuration) {
configuration
.add(ForbidFraming.class, new FixedExtractor<ForbidFraming>(
FnordSymbols.FORBID_FRAMING));
}
-</plain-text-body><p>If the ForbidFraming annotation had attributes, we would
have provided an implementation of MetaDataExtractor that examined those
attributes to set the meta-data value. Since it has no attributes, the <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/meta/FixedExtractor.html">FixedExtractor</a>
class can be used. The argument is the meta-data key, and the default value is
"true".</p><h2
id="Meta-ProgrammingPageContent-PluggingIntoPageRendering">Plugging Into Page
Rendering</h2><p>The work we ultimately want to do occurs when rendering a
page. Tapestry defines a <a href="pipelinebuilder-service.html">pipeline</a>
for that overall process. The point of a pipeline is that we can add filters to
it. We'll add a filter that checks for the meta-data key and adds the response
header and JavaScript.</p><p>The service is <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/s
ervices/MarkupRenderer.html">MarkupRenderer</a>, which (being a pipeline
service), takes a configuration of filters (in this case, <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/MarkupRendererFilter.html">MarkupRendererFilter</a>.</p><p>We
contribute into the pipeline; the order is important: since the filter will
need to write JavaScript, it must be added <em>after</em> the built-in filter
that provides the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/javascript/JavaScriptSupport.html">JavaScriptSupport</a>
environmental object.</p><parameter
ac:name="language">java</parameter><parameter
ac:name="lang">java</parameter><parameter
ac:name="title">ForbidFramingModule.java (partial)</parameter><plain-text-body>
@Contribute(MarkupRenderer.class)
+</pre>
+</div></div><p>If the ForbidFraming annotation had attributes, we would have
provided an implementation of MetaDataExtractor that examined those attributes
to set the meta-data value. Since it has no attributes, the <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/meta/FixedExtractor.html">FixedExtractor</a>
class can be used. The argument is the meta-data key, and the default value is
"true".</p><h2
id="Meta-ProgrammingPageContent-PluggingIntoPageRendering">Plugging Into Page
Rendering</h2><p>The work we ultimately want to do occurs when rendering a
page. Tapestry defines a <a
href="meta-programming-page-content.html">pipeline</a> for that overall
process. The point of a pipeline is that we can add filters to it. We'll add a
filter that checks for the meta-data key and adds the response header and
JavaScript.</p><p>The service is <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/s
ervices/MarkupRenderer.html">MarkupRenderer</a>, which (being a pipeline
service), takes a configuration of filters (in this case, <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/MarkupRendererFilter.html">MarkupRendererFilter</a>.</p><p>We
contribute into the pipeline; the order is important: since the filter will
need to write JavaScript, it must be added <em>after</em> the built-in filter
that provides the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/javascript/JavaScriptSupport.html">JavaScriptSupport</a>
environmental object.</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>ForbidFramingModule.java (partial)</b></div><div class="codeContent
panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Contribute(MarkupRenderer.class)
public static void addFilter(
OrderedConfiguration<MarkupRendererFilter> configuration) {
configuration.addInstance("ForbidFraming", ForbidFramingFilter.class,
"after:JavascriptSupport");
}
-</plain-text-body><p>How do you know what filters are built-in and where to
add your own? The right starting point is the JavaDoc for the method of
TapestryModule that contributes the base set: <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/modules/TapestryModule.html">contributeMarkupRenderer()</a></p><h2
id="Meta-ProgrammingPageContent-ImplementingtheFilter">Implementing the
Filter</h2><p>Everything comes together in the filter:</p><parameter
ac:name="language">java</parameter><parameter
ac:name="title">ForbidFramingFilter.java</parameter><plain-text-body>package
com.fnord.services.forbidframing;
+</pre>
+</div></div><p>How do you know what filters are built-in and where to add your
own? The right starting point is the JavaDoc for the method of TapestryModule
that contributes the base set: <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/modules/TapestryModule.html">contributeMarkupRenderer()</a></p><h2
id="Meta-ProgrammingPageContent-ImplementingtheFilter">Implementing the
Filter</h2><p>Everything comes together in the filter:</p><div class="code
panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>ForbidFramingFilter.java</b></div><div
class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">package com.fnord.services.forbidframing;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.ioc.annotations.Inject;
@@ -183,13 +205,16 @@ public class ForbidFramingFilter impleme
}
}
-</plain-text-body><p>There's a bit going on in this short piece of code. The
heart of the code is the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/MetaDataLocator.html">MetaDataLocator</a>
service; given a meta-data key and a page name, it can not only extract the
value, but then <a href="ioc-coerce.html">coerce</a> it to a desired type, all
in one go.</p><p>How do we know which page is being rendered? Before Tapestry
5.2 that was a small challenge, but 5.2 adds a method to <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/RequestGlobals.html#getActivePageName()">RequestGlobals</a>
for this exact purpose.</p><p>Both Request and JavaScriptSupport are
per-thread/per-request services. You don't see that here, because that's part
of the service definition, and invisible to the consumer code, as
here.</p><p>Of course, it is vitally important that the filter re-invoke <code>
markup()</code> on the next renderer in the pipeline (you can see that as the
last line of the method).</p><p>This code makes one assumption: that the fnord
application's Layout component added fnord.js to every page. That's necessary
for the JavaScript that's added:</p><parameter
ac:name="language">js</parameter><parameter
ac:name="lang">javascript</parameter><parameter ac:name="title">fnord.js
(partial)</parameter><plain-text-body>Fnord = {
+</pre>
+</div></div><p>There's a bit going on in this short piece of code. The heart
of the code is the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/MetaDataLocator.html">MetaDataLocator</a>
service; given a meta-data key and a page name, it can not only extract the
value, but then <a href="meta-programming-page-content.html">coerce</a> it to
a desired type, all in one go.</p><p>How do we know which page is being
rendered? Before Tapestry 5.2 that was a small challenge, but 5.2 adds a method
to <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/RequestGlobals.html#getActivePageName()">RequestGlobals</a>
for this exact purpose.</p><p>Both Request and JavaScriptSupport are
per-thread/per-request services. You don't see that here, because that's part
of the service definition, and invisible to the consumer code, as
here.</p><p>Of course, it is vitally important that the filter re-
invoke <code>markup()</code> on the next renderer in the pipeline (you can see
that as the last line of the method).</p><p>This code makes one assumption:
that the fnord application's Layout component added fnord.js to every page.
That's necessary for the JavaScript that's added:</p><div class="code panel
pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>fnord.js (partial)</b></div><div
class="codeContent panelContent pdl">
+<pre class="brush: js; gutter: false; theme: Default"
style="font-size:12px;">Fnord = {
popOutOfFrame : function() {
if (top != self)
top.location.replace(location);
}
}
-</plain-text-body><h2
id="Meta-ProgrammingPageContent-Conclusion">Conclusion</h2><p>That's it: with
the above code, simply adding the @ForbidFraming annotation to a page will add
the response header and associated JavaScript; no inheritance hassles. This
basic pattern can be applied to a wide range of cross-cutting concerns, such as
security, transaction management, logging, or virtually any other kind of
situation that would normally be solved with inheritance or ugly boilerplate
code.</p><rich-text-body><p>The code in this example was designed for Tapestry
version 5.2 and later.</p></rich-text-body></div>
+</pre>
+</div></div><h2
id="Meta-ProgrammingPageContent-Conclusion">Conclusion</h2><p>That's it: with
the above code, simply adding the @ForbidFraming annotation to a page will add
the response header and associated JavaScript; no inheritance hassles. This
basic pattern can be applied to a wide range of cross-cutting concerns, such as
security, transaction management, logging, or virtually any other kind of
situation that would normally be solved with inheritance or ugly boilerplate
code.</p><div class="confluence-information-macro
confluence-information-macro-note"><span class="aui-icon aui-icon-small
aui-iconfont-warning confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>The code in this example was
designed for Tapestry version 5.2 and later.</p></div></div></div>
</div>
<div class="clearer"></div>
Modified:
websites/production/tapestry/content/overriding-exception-reporting.html
==============================================================================
--- websites/production/tapestry/content/overriding-exception-reporting.html
(original)
+++ websites/production/tapestry/content/overriding-exception-reporting.html
Sat Feb 3 18:21:36 2018
@@ -45,13 +45,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">Overriding Exception Reporting</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">Overriding Exception Reporting</h1></div>
+
+</div>
<div class="clearer"></div>
</div>
@@ -63,25 +76,49 @@
</div>
<div id="content">
- <div id="ConfluenceContent"><p> </p><p>One of Tapestry's
best features is its comprehensive exception reporting. The level of detail is
impressive and useful.</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="error-page-recipe.html">Error Page Recipe</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="specific-errors-faq.html">Specific Errors 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="overriding-exception-reporting.html">Overriding Exception
Reporting</a>
- </div> </li></ul></div><p>Of course, one of the first questions anyone asks
is "How do I turn it off?" This exception reporting is very helpful for
developers but its easy to see it as terrifying for potential users. Catching
runtime exceptions can be a very useful way of handling rarely occurring
exceptions even in production, and there's no reason to throw away Tapestry's
default error reporting just to handle a few specific exceptions. From version
5.4 (for previous versions, the same functionality is available as a <a
class="external-link"
href="http://www.tynamo.org/tapestry-exceptionpage+guide/"
rel="nofollow">third-party module tapestry-exceptionpage</a>), you can
contribute exception handles and/or exception pages for specific exception
types. Refer back to <a href="runtime-exceptions.html">Runtime Exceptions</a>
page for more information. Read on if you want to completely replace Tapestry's
default exception handling.</p><h2 id="OverridingExceptionReporting-Version1:Rep
lacingtheExceptionReportPage">Version 1: Replacing the Exception Report
Page</h2><p>Let's start with a page that fires an exception from an event
handler method.</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>ActionFail.tml</b></div><div class="codeContent panelContent pdl">
+ <div
id="ConfluenceContent"><p> </p><p></p><p> </p><p>One of Tapestry's
best features is its comprehensive exception reporting. The level of detail is
impressive and useful.</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="error-page-recipe.html">Error Page Recipe</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="specific-errors-faq.html">Specific Errors
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="overriding-exception-reporting.html">Overriding Exception Reporting</a>
+
+
+ </div>
+ </li></ul>
+</div>
+
+
+<p>Of course, one of the first questions anyone asks is "How do I turn it
off?" This exception reporting is very helpful for developers but its easy to
see it as terrifying for potential users. Catching runtime exceptions can be a
very useful way of handling rarely occurring exceptions even in production, and
there's no reason to throw away Tapestry's default error reporting just to
handle a few specific exceptions. From version 5.4 (for previous versions, the
same functionality is available as a <a class="external-link"
href="http://www.tynamo.org/tapestry-exceptionpage+guide/"
rel="nofollow">third-party module tapestry-exceptionpage</a>), you can
contribute exception handles and/or exception pages for specific exception
types. Refer back to <a href="overriding-exception-reporting.html">Overriding
Exception Reporting</a> page for more information. Read on if you want to
completely replace Tapestry's default exception handling.</p><h2
id="OverridingExceptionReporting-Version1:Repl
acingtheExceptionReportPage">Version 1: Replacing the Exception Report
Page</h2><p>Let's start with a page that fires an exception from an event
handler method.</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>ActionFail.tml</b></div><div class="codeContent panelContent pdl">
<pre class="brush: xml; gutter: false; theme: Default"
style="font-size:12px;"> <html
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd" t:type="layout"
title="Action Fail">
<p>
<t:actionlink t:id="fail" class="btn btn-large
btn-warning">Click for Exception</t:actionlink>
@@ -96,7 +133,7 @@ public class ActionFail {
}
}
</pre>
-</div></div><p>With production mode disabled, clicking the link displays the
default exception report page:</p><p><span
class="confluence-embedded-file-wrapper image-center-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image
confluence-content-image-border image-center" width="500"
src="overriding-exception-reporting.data/actionfail_-_top.png"></span></p><p><span
class="confluence-embedded-file-wrapper image-center-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image
confluence-content-image-border image-center" width="500"
src="overriding-exception-reporting.data/Application_Exception.png"></span></p><p> </p><p>The
easy way to override the exception report is to provide an ExceptionReport
page that overrides the one provided with the framework.</p><p>This is as easy
as providing a page named "ExceptionReport". It must implement the <a
class="external-link"
href="http://tapestry.apache.org/tapestry5/apidocs/org/apache/tapest
ry5/services/ExceptionReporter.html">ExceptionReporter</a> interface.</p><div
class="code panel pdl" style="border-width: 1px;"><div class="codeHeader
panelHeader pdl" style="border-bottom-width:
1px;"><b>ExceptionReport.tml</b></div><div class="codeContent panelContent pdl">
+</div></div><p>With production mode disabled, clicking the link displays the
default exception report page:</p><p> </p><p><span
class="confluence-embedded-file-wrapper image-center-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image
confluence-content-image-border image-center" width="500"
src="overriding-exception-reporting.data/actionfail_-_top.png"></span></p><p><span
class="confluence-embedded-file-wrapper image-center-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image
confluence-content-image-border image-center" width="500"
src="overriding-exception-reporting.data/Application_Exception.png"></span></p><p> </p><p>The
easy way to override the exception report is to provide an ExceptionReport
page that overrides the one provided with the framework.</p><p>This is as easy
as providing a page named "ExceptionReport". It must implement the <a
class="external-link" href="http://tapestry.apache.org/tapestry5/apidocs/org/
apache/tapestry5/services/ExceptionReporter.html">ExceptionReporter</a>
interface.</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>ExceptionReport.tml</b></div><div class="codeContent panelContent pdl">
<pre class="brush: xml; gutter: false; theme: Default"
style="font-size:12px;"><html t:type="layout" title="Exception"
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd">
@@ -135,7 +172,7 @@ public class ExceptionReport implements
}
}
</pre>
-</div></div><p>The end result is a customized exception report
page.</p><p><span class="confluence-embedded-file-wrapper image-center-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image
confluence-content-image-border image-center" width="500"
src="overriding-exception-reporting.data/customer_exception_report_-_open_1.png"></span></p><h2
id="OverridingExceptionReporting-Version2:OverridingtheRequestExceptionHandler">Version
2: Overriding the RequestExceptionHandler</h2><p>The previous example will
display a link back to the Index page of the application. Another alternative
is to display the error <on> the Index page. This requires a different
approach: overriding the service responsible for reporting request
exceptions.</p><p>The service <a class="external-link"
href="http://tapestry.apache.org/tapestry5/apidocs/org/apache/tapestry5/services/RequestExceptionHandler.html">RequestExceptionHandler</a>
is responsible for this.</p><p>By replacing the de
fault implementation of this service with our own implementation, we can take
control over exactly what happens when a request exception occurs.</p><p>We'll
do this in two steps. First, we'll extend the Index page to serve as an
ExceptionReporter. Second, we'll override the default RequestExceptionHandler
to use the Index page instead of the ExceptionReport page. Of course, this is
just one approach.</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>Index.tml (partial)</b></div><div class="codeContent panelContent pdl">
+</div></div><p>The end result is a customized exception report
page.</p><p> </p><p><span class="confluence-embedded-file-wrapper
image-center-wrapper confluence-embedded-manual-size"><img
class="confluence-embedded-image confluence-content-image-border image-center"
width="500"
src="overriding-exception-reporting.data/customer_exception_report_-_open_1.png"></span></p><h2
id="OverridingExceptionReporting-Version2:OverridingtheRequestExceptionHandler">Version
2: Overriding the RequestExceptionHandler</h2><p>The previous example will
display a link back to the Index page of the application. Another alternative
is to display the error <on> the Index page. This requires a different
approach: overriding the service responsible for reporting request
exceptions.</p><p>The service <a class="external-link"
href="http://tapestry.apache.org/tapestry5/apidocs/org/apache/tapestry5/services/RequestExceptionHandler.html">RequestExceptionHandler</a>
is responsible for this.</p><p>By rep
lacing the default implementation of this service with our own implementation,
we can take control over exactly what happens when a request exception
occurs.</p><p>We'll do this in two steps. First, we'll extend the Index page to
serve as an ExceptionReporter. Second, we'll override the default
RequestExceptionHandler to use the Index page instead of the ExceptionReport
page. Of course, this is just one approach.</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>Index.tml (partial)</b></div><div
class="codeContent panelContent pdl">
<pre class="brush: xml; gutter: false; theme: Default"
style="font-size:12px;"> <t:if test="message">
<div class="panel panel-danger">
<div class="panel-heading">An exception has occurred.</div>
@@ -191,7 +228,7 @@ public class ExceptionReport implements
configuration.add(RequestExceptionHandler.class, handler);
}
</pre>
-</div></div><p>First we define the new service using a service builder method.
This is an alternative to the <code>bind()</code> method; we define the
service, its interface type (the return type of the method) and the service id
(the part that follows "build" is the method name) and provide the
implementation inline. A service builder method must return the service
implementation, here implemented as an inner class.</p><p>The Logger resource
that is passed into the builder method is the Logger appropriate for the
service. ResponseRenderer and ComponentSource are two services defined by
Tapestry.</p><p>With this in place, there are now two different services that
implement the RequestExceptionHandler interface: the default one built into
Tapestry (whose service id is "RequestExceptionHandler") and the new one
defined in this module, "AppRequestExceptionHandler"). Without a little more
work, Tapestry will be unable to determine which one to use when an exception
does occur.</p><p>Tap
estry has a pipeline for resolving injected dependencies; the ServiceOverride
service is one part of that pipeline. Contributions to it are used to override
an existing service, when the injection is exclusively by type.</p><p>Here we
inject the AppRequestExceptionHandler service and contribute it as the override
for type RequestExceptionHandler. The @Local annotation is used to select the
RequestHandler service defined by this module, AppModule. Once contributed into
ServiceOverride, it becomes the default service injected throughout the
Registry.</p><p>This finally brings us to the point where we can see the
result:</p><p><span class="confluence-embedded-file-wrapper
image-center-wrapper confluence-embedded-manual-size"><img
class="confluence-embedded-image confluence-content-image-border image-center"
height="375" width="500"
src="overriding-exception-reporting.data/index_as_excepton_report.png"></span></p><h2
id="OverridingExceptionReporting-Version3:DecoratingtheRequestExceptio
nHandler">Version 3: Decorating the RequestExceptionHandler</h2><p>A third
option is available: we don't define a <em>new</em> service, but instead
<em>decorate</em> the existing RequestExceptionHandler service. This approach
means we don't have to make a contribution to the ServiceOverride
service.</p><p>Service decoration is a powerful facility of Tapestry that is
generally used to "wrap" an existing service with an interceptor that provides
new functionality such as logging, security, transaction management or other
cross-cutting concerns. The interceptor is an object that implements the same
interface as the service being decorated, and usually delegates method
invocations to it.</p><p>However, there's no requirement that an interceptor
for a service actually invoke methods on the service; here we contribute a new
implementation that <em>replaces</em> the original:</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">
+</div></div><p>First we define the new service using a service builder method.
This is an alternative to the <code>bind()</code> method; we define the
service, its interface type (the return type of the method) and the service id
(the part that follows "build" is the method name) and provide the
implementation inline. A service builder method must return the service
implementation, here implemented as an inner class.</p><p>The Logger resource
that is passed into the builder method is the Logger appropriate for the
service. ResponseRenderer and ComponentSource are two services defined by
Tapestry.</p><p>With this in place, there are now two different services that
implement the RequestExceptionHandler interface: the default one built into
Tapestry (whose service id is "RequestExceptionHandler") and the new one
defined in this module, "AppRequestExceptionHandler"). Without a little more
work, Tapestry will be unable to determine which one to use when an exception
does occur.</p><p>Tap
estry has a pipeline for resolving injected dependencies; the ServiceOverride
service is one part of that pipeline. Contributions to it are used to override
an existing service, when the injection is exclusively by type.</p><p>Here we
inject the AppRequestExceptionHandler service and contribute it as the override
for type RequestExceptionHandler. The @Local annotation is used to select the
RequestHandler service defined by this module, AppModule. Once contributed into
ServiceOverride, it becomes the default service injected throughout the
Registry.</p><p>This finally brings us to the point where we can see the
result:</p><p> </p><p><span class="confluence-embedded-file-wrapper
image-center-wrapper confluence-embedded-manual-size"><img
class="confluence-embedded-image confluence-content-image-border image-center"
height="375" width="500"
src="overriding-exception-reporting.data/index_as_excepton_report.png"></span></p><h2
id="OverridingExceptionReporting-Version3:DecoratingtheRe
questExceptionHandler">Version 3: Decorating the
RequestExceptionHandler</h2><p>A third option is available: we don't define a
<em>new</em> service, but instead <em>decorate</em> the existing
RequestExceptionHandler service. This approach means we don't have to make a
contribution to the ServiceOverride service.</p><p>Service decoration is a
powerful facility of Tapestry that is generally used to "wrap" an existing
service with an interceptor that provides new functionality such as logging,
security, transaction management or other cross-cutting concerns. The
interceptor is an object that implements the same interface as the service
being decorated, and usually delegates method invocations to it.</p><p>However,
there's no requirement that an interceptor for a service actually invoke
methods on the service; here we contribute a new implementation that
<em>replaces</em> the original:</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeHeader panelHeader pdl" styl
e="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 RequestExceptionHandler
decorateRequestExceptionHandler(
final Logger logger,
final ResponseRenderer renderer,
@@ -217,7 +254,7 @@ public class ExceptionReport implements
};
}
</pre>
-</div></div><p>As with service builder methods and service configuration
method, decorator methods are recognized by the "decorate" prefix on the method
name. As used here, the rest of the method name is used to identify the service
to be decorated (there are other options that allow a decorator to be applied
to many different services).</p><p>A change in this version is that when in
development mode (that is, when <em>not</em> in production mode) we use the
normal implementation. Returning null from a service decoration method
indicates that the decorator chooses not to decorate.</p><p>The Logger injected
here is the Logger for the service being decorated, the default
RequestExceptionHandler service.</p><p>Otherwise, we return an interceptor
whose implementation is the same as the new service in version #2.</p><p>The
end result is that in development mode we get the full exception report, and in
production mode we get an abbreviated message on the application's Index
page.</p></div
>
+</div></div><p>As with service builder methods and service configuration
method, decorator methods are recognized by the "decorate" prefix on the method
name. As used here, the rest of the method name is used to identify the service
to be decorated (there are other options that allow a decorator to be applied
to many different services).</p><p>A change in this version is that when in
development mode (that is, when <em>not</em> in production mode) we use the
normal implementation. Returning null from a service decoration method
indicates that the decorator chooses not to decorate.</p><p>The Logger injected
here is the Logger for the service being decorated, the default
RequestExceptionHandler service.</p><p>Otherwise, we return an interceptor
whose implementation is the same as the new service in version #2.</p><p>The
end result is that in development mode we get the full exception report, and in
production mode we get an abbreviated message on the application's Index
page.</p><p>&#
160;</p></div>
</div>
<div class="clearer"></div>
Modified:
websites/production/tapestry/content/page-and-component-classes-faq.html
==============================================================================
--- websites/production/tapestry/content/page-and-component-classes-faq.html
(original)
+++ websites/production/tapestry/content/page-and-component-classes-faq.html
Sat Feb 3 18:21:36 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,11 +77,14 @@
</div>
<div id="content">
- <div
id="ConfluenceContent"><plain-text-body>{scrollbar}</plain-text-body><h2
id="PageAndComponentClassesFAQ-PageAndComponentClasses">Page And Component
Classes</h2><p>Main article: <a href="component-classes.html">Component
Classes</a></p><h3
id="PageAndComponentClassesFAQ-What'sthedifferencebetweenapageandacomponent?">What's
the difference between a page and a component?</h3><p>There's very little
difference between the two. Pages classes must be in the
<em>root-package</em>.<code>pages</code> package; components must be in the
<em>root-package</em>.<code>components</code>. Pages may provide event handlers
for certain page-specific events (such as activate and passivate). Components
may have parameters.</p><p>Other than that, they are more equal than they are
different. They may have templates or may render themselves in code (pages
usually have a template, components are more likely to render only in
code).</p><p>The major difference is that Tapestry page templat
es may be stored in the web context directory, as if they were static files
(they can't be accessed from the client however; a specific rule prevents
access to files with the <code>.tml</code> extension).</p><rich-text-body><p>It
is possible that this feature may be removed in a later release. It is
preferred that page templates be stored on the classpath, like component
templates.</p></rich-text-body><h3
id="PageAndComponentClassesFAQ-HowdoIstoremypageclassesinadifferentpackage?">How
do I store my page classes in a different package?</h3><p>Tapestry is very
rigid here; you can't. Page classes must go in
<em>root-package</em>.<code>pages</code>, component classes in
<em>root-package</em>.<code>components</code>, etc.</p><p>You are allowed to
create sub-packages, to help organize your code better and more logically. For
example, you might have
<em>root-package</em>.<code>pages.account.ViewAccount</code>, which would have
the page name "account/viewaccount". (<span style="line-height:
1.4285715;">Tapestry would also create an alias "account/view", by stripping
off the redundant "account" suffix. Either name is equally valid in your code,
and Tapestry will use the shorter name, "account/view" in
URLs.)</span></p><p>In addition, it is possible to define additional root
packages for the application:</p><parameter
ac:name="controls">true</parameter><parameter
ac:name="linenumbers">true</parameter><plain-text-body>public static void
contributeComponentClassResolver(Configuration<LibraryMapping>
configuration) {
+ <div id="ConfluenceContent"><h2
id="PageAndComponentClassesFAQ-PageAndComponentClasses">Page And Component
Classes</h2><p>Main article: <a
href="page-and-component-classes-faq.html">Page And Component Classes
FAQ</a></p><h3
id="PageAndComponentClassesFAQ-What'sthedifferencebetweenapageandacomponent?">What's
the difference between a page and a component?</h3><p>There's very little
difference between the two. Pages classes must be in the
<em>root-package</em>.<code>pages</code> package; components must be in the
<em>root-package</em>.<code>components</code>. Pages may provide event handlers
for certain page-specific events (such as activate and passivate). Components
may have parameters.</p><p>Other than that, they are more equal than they are
different. They may have templates or may render themselves in code (pages
usually have a template, components are more likely to render only in
code).</p><p>The major difference is that Tapestry page templates may be stored
in
the web context directory, as if they were static files (they can't be
accessed from the client however; a specific rule prevents access to files with
the <code>.tml</code> extension).</p><div class="confluence-information-macro
confluence-information-macro-warning"><span class="aui-icon aui-icon-small
aui-iconfont-error confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>It is possible that this feature
may be removed in a later release. It is preferred that page templates be
stored on the classpath, like component templates.</p></div></div><h3
id="PageAndComponentClassesFAQ-HowdoIstoremypageclassesinadifferentpackage?">How
do I store my page classes in a different package?</h3><p>Tapestry is very
rigid here; you can't. Page classes must go in
<em>root-package</em>.<code>pages</code>, component classes in
<em>root-package</em>.<code>components</code>, etc.</p><p>You are allowed to
create sub-packages, to help organize your code better and mor
e logically. For example, you might have
<em>root-package</em>.<code>pages.account.ViewAccount</code>, which would have
the page name "account/viewaccount". (<span>Tapestry would also create an alias
"account/view", by stripping off the redundant "account" suffix. Either name is
equally valid in your code, and Tapestry will use the shorter name,
"account/view" in URLs.)</span></p><p>In addition, it is possible to define
additional root packages for the application:</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: true; theme: Default"
style="font-size:12px;">public static void
contributeComponentClassResolver(Configuration<LibraryMapping>
configuration) {
configuration.add(new LibraryMapping("", "com.example.app.tasks"));
configuration.add(new LibraryMapping("", "com.example.app.chat"));
}
-</plain-text-body><p>LibraryMappings are used to resolve a library prefix to
one or more package names. The empty string represents the application itself;
the above example adds two additional root packages; you might see additional
pages under <code>com.example.app.tasks.pages</code>, for
example.</p><rich-text-body><p>Tapestry doesn't check for name collisions, and
the order the packages are searched for pages and components is not defined. In
general, if you can get by with a single root package for your application,
that is better.</p></rich-text-body><h3
id="PageAndComponentClassesFAQ-Whydomyinstancevariableshavetobeprivate?">Why do
my instance variables have to be private?</h3><p><em>In Tapestry 5.3.1 and
earlier all instance variables must be private. Starting in version 5.3.2
instance variables can also be protected or package private (that is, not
public), or they can even be public if <code>final</code> or annotated with the
deprecated @Retain.</em></p><p>Tapestry does a
large amount of transformation to your simple POJO classes as it loads them
into memory. In many cases, it must locate every read or write of an instance
variable and change its behavior; for example, reading a field that is a
component parameter will cause a property of the containing page or component
to be read.</p><p>Restricting the scope of fields allows Tapestry to do the
necessary processing one class at a time, as needed, at runtime. More complex
Aspect Orient Programming systems such as AspectJ can perform similar
transformations (and much more complex ones), but they require a dedicated
build step (or the introduction of a JVM agent).</p><h3
id="PageAndComponentClassesFAQ-Whydon'tmyinformalparametersshowupintherenderedmarkup?">Why
don't my informal parameters show up in the rendered markup?</h3><p>Getting
informal parameters to work is in two steps. First, you must make a call to the
<code>ComponentResources.renderInformalParameters()</code> method, but just as
importantly
, you must tell Tapestry that you want the component to support informal
parameters, using the <code>SupportsInformalParameters</code> annotation.
Here's a hypothetical component that displays an image based on the value of a
<code>Image</code> object (presumably, a database entity):</p><parameter
ac:name="controls">true</parameter><parameter
ac:name="linenumbers">true</parameter><plain-text-body>@SupportsInformalParameters
+</pre>
+</div></div><p>LibraryMappings are used to resolve a library prefix to one or
more package names. The empty string represents the application itself; the
above example adds two additional root packages; you might see additional pages
under <code>com.example.app.tasks.pages</code>, for example.</p><div
class="confluence-information-macro confluence-information-macro-warning"><span
class="aui-icon aui-icon-small aui-iconfont-error
confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>Tapestry doesn't check for name
collisions, and the order the packages are searched for pages and components is
not defined. In general, if you can get by with a single root package for your
application, that is better.</p></div></div><h3
id="PageAndComponentClassesFAQ-Whydomyinstancevariableshavetobeprivate?">Why do
my instance variables have to be private?</h3><p><em>In Tapestry 5.3.1 and
earlier all instance variables must be private. Starting in version 5.3.2 inst
ance variables can also be protected or package private (that is, not public),
or they can even be public if <code>final</code> or annotated with the
deprecated @Retain.</em></p><p>Tapestry does a large amount of transformation
to your simple POJO classes as it loads them into memory. In many cases, it
must locate every read or write of an instance variable and change its
behavior; for example, reading a field that is a component parameter will cause
a property of the containing page or component to be read.</p><p>Restricting
the scope of fields allows Tapestry to do the necessary processing one class at
a time, as needed, at runtime. More complex Aspect Orient Programming systems
such as AspectJ can perform similar transformations (and much more complex
ones), but they require a dedicated build step (or the introduction of a JVM
agent).</p><h3
id="PageAndComponentClassesFAQ-Whydon'tmyinformalparametersshowupintherenderedmarkup?">Why
don't my informal parameters show up in the rende
red markup?</h3><p>Getting informal parameters to work is in two steps. First,
you must make a call to the
<code>ComponentResources.renderInformalParameters()</code> method, but just as
importantly, you must tell Tapestry that you want the component to support
informal parameters, using the <code>SupportsInformalParameters</code>
annotation. Here's a hypothetical component that displays an image based on the
value of a <code>Image</code> object (presumably, a database entity):</p><div
class="code panel pdl" style="border-width: 1px;"><div class="codeContent
panelContent pdl">
+<pre class="brush: java; gutter: true; theme: Default"
style="font-size:12px;">@SupportsInformalParameters
public class DBImage
{
@Parameter(required=true)
@@ -91,12 +104,31 @@ public class DBImage
return false;
}
}
-</plain-text-body><h3
id="PageAndComponentClassesFAQ-WhydoIgetjava.lang.LinkageErrorwhenIinvokepublicmethodsofmypageclasses?">Why
do I get java.lang.LinkageError when I invoke public methods of my page
classes?</h3><p>In Tapestry, there are always <em>two</em> versions of page (or
component) classes. The first version is the version loaded by standard class
loader: the simple POJO version that you wrote.</p><p>The second version is
much more complicated; it's the transformed version of your code, with lots of
extra hooks and changes to allow the class to operate inside Tapestry. This
includes implementing new interfaces and methods, adding new constructors, and
changing access to existing fields and methods.</p><p>Although these two
classes have the same fully qualified class name, they are distinct classes
because they are loaded by different class loaders.</p><p><parameter
ac:name="size">L</parameter><parameter ac:name="name">Class
Loaders</parameter></p><p>In a Tapestry applicati
on, most application classes are loaded from the middle class loader.
Additional class loaders are used<br clear="none"> to support live service
reloading, and live component reloading (along with component class
transformation).</p><p>When a page or component is passed as a parameter to a
service, a failure occurs (how it is reported varies in different JDK releases)
because of the class mismatch.</p><p>The solution is to define an interface
with the methods that the service will invoke on the page or component
instance. The service will expect an object implementing the interface (and
doesn't care what class loader loaded the implementing class).</p><p>Just be
sure to put the interface class in a non-controlled package, such as your
application's <em>root-package</em> (and <strong>not</strong>
<em>root-package</em>.<code>pages</code>).</p><h3
id="PageAndComponentClassesFAQ-Whichisbetter,usingmagicmethodnames(i.e.,beginRender())orannotations(i.e.BeginRender)?">Which
is better, usin
g magic method names (i.e., <code>beginRender()</code>) or annotations (i.e.
<code>BeginRender</code>)?</h3><p>There is no single best way; this is where
your taste may vary. Historically, the annotations came first, and the method
naming conventions came later.</p><p>The advantage of using the method naming
conventions is that the method names are more concise, which fewer characters
to type, and fewer classes to import.</p><p>The main disadvantage of the method
naming conventions is that the method names are not meaningful.
<code>onSuccessFromLoginForm()</code> is a less meaningful name than
<code>storeUserCredentialsAndReturnToProductsPage()</code>, for
example.</p><p>The second disadvantage is you are more susceptible to
off-by-a-character errors. For example, <code>onSucessFromLoginForm()</code>
will <em>never</em> be called because the event name is misspelled; this would
not happen using the annotation approach:</p><parameter
ac:name="controls">true</parameter><parameter ac:n
ame="linenumbers">true</parameter><plain-text-body>
@OnEvent(value=EventConstants.SUCCESS, component="loginForm")
+</pre>
+</div></div><h3
id="PageAndComponentClassesFAQ-WhydoIgetjava.lang.LinkageErrorwhenIinvokepublicmethodsofmypageclasses?">Why
do I get java.lang.LinkageError when I invoke public methods of my page
classes?</h3><p>In Tapestry, there are always <em>two</em> versions of page (or
component) classes. The first version is the version loaded by standard class
loader: the simple POJO version that you wrote.</p><p>The second version is
much more complicated; it's the transformed version of your code, with lots of
extra hooks and changes to allow the class to operate inside Tapestry. This
includes implementing new interfaces and methods, adding new constructors, and
changing access to existing fields and methods.</p><p>Although these two
classes have the same fully qualified class name, they are distinct classes
because they are loaded by different class loaders.</p><p>
+
+
+
+
+<span class="gliffy-container" id="gliffy-container-23527573-260"
data-fullwidth="750" data-ceoid="23335008"
data-edit="${diagramEditLink.getLinkUrl()}"
data-full="${diagramZoomLink.getLinkUrl()}" data-filename="Class Loaders">
+
+ <map id="gliffy-map-23527573-7566" name="gliffy-map-23527573-7566"></map>
+
+ <img class="gliffy-image" id="gliffy-image-23527573-260" width="750"
height="425" data-full-width="750" data-full-height="425"
src="https://cwiki.apache.org/confluence/download/attachments/23335008/Class%20Loaders.png?version=4&modificationDate=1283534469000&api=v2"
alt="Class Loaders" usemap="#gliffy-map-23527573-7566">
+
+ <map class="gliffy-dynamic" id="gliffy-dynamic-map-23527573-260"
name="gliffy-dynamic-map-23527573-260"></map>
+</span>
+
+
+</p><p>In a Tapestry application, most application classes are loaded from the
middle class loader. Additional class loaders are used<br clear="none"> to
support live service reloading, and live component reloading (along with
component class transformation).</p><p>When a page or component is passed as a
parameter to a service, a failure occurs (how it is reported varies in
different JDK releases) because of the class mismatch.</p><p>The solution is to
define an interface with the methods that the service will invoke on the page
or component instance. The service will expect an object implementing the
interface (and doesn't care what class loader loaded the implementing
class).</p><p>Just be sure to put the interface class in a non-controlled
package, such as your application's <em>root-package</em> (and
<strong>not</strong> <em>root-package</em>.<code>pages</code>).</p><h3
id="PageAndComponentClassesFAQ-Whichisbetter,usingmagicmethodnames(i.e.,beginRender())orannotations(i.e.BeginR
ender)?">Which is better, using magic method names (i.e.,
<code>beginRender()</code>) or annotations (i.e.
<code>BeginRender</code>)?</h3><p>There is no single best way; this is where
your taste may vary. Historically, the annotations came first, and the method
naming conventions came later.</p><p>The advantage of using the method naming
conventions is that the method names are more concise, which fewer characters
to type, and fewer classes to import.</p><p>The main disadvantage of the method
naming conventions is that the method names are not meaningful.
<code>onSuccessFromLoginForm()</code> is a less meaningful name than
<code>storeUserCredentialsAndReturnToProductsPage()</code>, for
example.</p><p>The second disadvantage is you are more susceptible to
off-by-a-character errors. For example, <code>onSucessFromLoginForm()</code>
will <em>never</em> be called because the event name is misspelled; this would
not happen using the annotation approach:</p><div class="code panel pdl" sty
le="border-width: 1px;"><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: true; theme: Default"
style="font-size:12px;"> @OnEvent(value=EventConstants.SUCCESS,
component="loginForm")
Object storeUserCredentialsAndReturnToProductsPage()
{
. . .
}
-</plain-text-body><p>The compiler will catch a misspelling of the constant
<code>SUCCESS</code>. Likewise, local constants can be defined for key
components, such as "loginForm".</p><rich-text-body><p>Ultimately, it's
developer choice. HLS prefers the method naming conventions in nearly all
cases, especially prototypes and demos, but can see that in some projects and
some teams, an annotation-only approach is best.</p></rich-text-body><h3
id="PageAndComponentClassesFAQ-WhydoIhavetoinjectapage?Whycan'tIjustcreateoneusingnew?">Why
do I have to inject a page? Why can't I just create one using
new?</h3><p>Tapestry tranforms your class at runtime. It tends to build a large
constructor for the class instance. Further, an instance of the class is
useless by itself, it must be wired together with its template and its
sub-components.</p><p>On top of that, Tapestry keeps just once instance of each
page in memory (since 5.2). It reworks the bytecode of the components so that a
single instance
can be shared across multiple request handling
threads.</p><plain-text-body>{scrollbar}</plain-text-body><p>____</p><p> </p><p> </p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p> </p><p> </p></div>
+</pre>
+</div></div><p>The compiler will catch a misspelling of the constant
<code>SUCCESS</code>. Likewise, local constants can be defined for key
components, such as "loginForm".</p><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>Ultimately, it's developer choice.
HLS prefers the method naming conventions in nearly all cases, especially
prototypes and demos, but can see that in some projects and some teams, an
annotation-only approach is best.</p></div></div><h3
id="PageAndComponentClassesFAQ-WhydoIhavetoinjectapage?Whycan'tIjustcreateoneusingnew?">Why
do I have to inject a page? Why can't I just create one using
new?</h3><p>Tapestry tranforms your class at runtime. It tends to build a large
constructor for the class instance. Further, an instance of the class is
useless by itself, it must be wired together wi
th its template and its sub-components.</p><p>On top of that, Tapestry keeps
just once instance of each page in memory (since 5.2). It reworks the bytecode
of the components so that a single instance can be shared across multiple
request handling
threads.____</p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p></p><div
class="display-footnotes"
data-footnotestodisplay="${footnotesToDisplay}"></div>
+<p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p></div>
</div>
<div class="clearer"></div>