Modified: websites/production/tapestry/content/service-advisors.html
==============================================================================
--- websites/production/tapestry/content/service-advisors.html (original)
+++ websites/production/tapestry/content/service-advisors.html Wed Sep 20
12:29:16 2017
@@ -27,6 +27,14 @@
</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>
+ SyntaxHighlighter.defaults['toolbar'] = false;
+ SyntaxHighlighter.all();
+ </script>
<link href="/styles/style.css" rel="stylesheet" type="text/css"/>
@@ -67,7 +75,8 @@
</div>
<div id="content">
- <div id="ConfluenceContent"><h1
id="ServiceAdvisors-ServiceAdvisors">Service Advisors</h1><p>Service advice
represents a powerful meta-programming facility available to services. In fact,
it is a kind of limited Aspect Oriented Programming.</p><p>Service advice
allows you to intercept method invocations on your services; you have the
ability to see what methods get invoked, what the parameters are. You can let
the normal code do it work, and then inspect or even adjust the return value,
or any thrown exceptions. And you can do this all in normal Java code.</p><p>A
common example of method-level service advice is to log method entry and exit,
complete with parameter values, return values, and thrown exceptions. Other
approaches include security checks, transaction management, and other broadly
spanning concerns.</p><p>Let's start with a (contrived) example. Let's say you
have an existing set of services that have methods that sometimes return null,
and you want them t
o return an empty string instead because you are getting some
NullPointerExceptions elsewhere in your application.</p><p>You could track down
the implementation of each service and fix the logic that provides a return
value ... or you could advise the methods:</p><plain-text-body> @Match("*")
+ <div id="ConfluenceContent"><h1
id="ServiceAdvisors-ServiceAdvisors">Service Advisors</h1><p>Service advice
represents a powerful meta-programming facility available to services. In fact,
it is a kind of limited Aspect Oriented Programming.</p><p>Service advice
allows you to intercept method invocations on your services; you have the
ability to see what methods get invoked, what the parameters are. You can let
the normal code do it work, and then inspect or even adjust the return value,
or any thrown exceptions. And you can do this all in normal Java code.</p><p>A
common example of method-level service advice is to log method entry and exit,
complete with parameter values, return values, and thrown exceptions. Other
approaches include security checks, transaction management, and other broadly
spanning concerns.</p><p>Let's start with a (contrived) example. Let's say you
have an existing set of services that have methods that sometimes return null,
and you want them t
o return an empty string instead because you are getting some
NullPointerExceptions elsewhere in your application.</p><p>You could track down
the implementation of each service and fix the logic that provides a return
value ... or you could advise the methods:</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;"> @Match("*")
public static void adviseNonNull(MethodAdviceReceiver receiver)
{
MethodAdvice advice = new MethodAdvice()
@@ -83,7 +92,9 @@
receiver.adviseAllMethods(advice);
};
-</plain-text-body><p>This is a method that is placed in a module class. Note
the terminology: <em>advise</em> is the verb ("to advise a method") and
<em>advice</em> is the noun ("with this advice"). The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/MethodAdviceReceiver.html">MethodAdviceReceiver</a>
is a wrapper around the service being advised: you can add advice to some or
all methods of the service, and also obtain the interface of the service. It is
automatically passed into service advisor methods.</p><p>See <a
href="injection-in-detail.html">Injection in Detail</a> for what can be
injected into a service advisor method.</p><p>Service advisor methods must have
a parameter of type MethodAdviceReceiver.</p><p>A service will often be advised
multiple times; any method may have any number of advice objects applied to it.
Some methods may not get any advice. All of this is acceptable.</p><p>Service
advisor methods are always voi
d methods (this is different from <a
href="tapestry-ioc-decorators.html">service decorator methods</a>).</p><p>The
@<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Match.html">Match</a>("*")
annotation indicates that this advice applies to all services (both your own,
and those defined by Tapestry). You will want to narrow down which services are
actually targeted in most cases.</p><p>Note that some services, especially
those built into Tapestry IoC, are marked as <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/PreventServiceDecoration.html">not
subject to decoration</a>, this applies to service advice as well as service
decoration.</p><p>The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/MethodAdvice.html">MethodAdvice</a>
interface is very simple; it receives an <a class="external-link"
href="http://t
apestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/Invocation.html">Invocation</a>
representing a method call. Invocation has methods for inspecting the type and
value of the parameters, and for overriding the values of the
parameters.</p><p>The call to <code>proceed()</code> allows the invocation to
continue; that is, the original method is invoked. If the method has been
advised multiple times, the call to proceed() may chain into the next
MethodAdvice object. In any case, after invoking <code>proceed()</code>, you
may inspect and override the result (the return value).</p><p>Advice is pretty
efficient, but it is still better to apply it only to methods that make sense.
We can improve the service advisor method in our example to only advise methods
that return String:</p><plain-text-body> @Match("*")
+</pre>
+</div></div><p>This is a method that is placed in a module class. Note the
terminology: <em>advise</em> is the verb ("to advise a method") and
<em>advice</em> is the noun ("with this advice"). The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/MethodAdviceReceiver.html">MethodAdviceReceiver</a>
is a wrapper around the service being advised: you can add advice to some or
all methods of the service, and also obtain the interface of the service. It is
automatically passed into service advisor methods.</p><p>See <a
href="injection-in-detail.html">Injection in Detail</a> for what can be
injected into a service advisor method.</p><p>Service advisor methods must have
a parameter of type MethodAdviceReceiver.</p><p>A service will often be advised
multiple times; any method may have any number of advice objects applied to it.
Some methods may not get any advice. All of this is acceptable.</p><p>Service
advisor methods are always void meth
ods (this is different from <a href="tapestry-ioc-decorators.html">service
decorator methods</a>).</p><p>The @<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Match.html">Match</a>("*")
annotation indicates that this advice applies to all services (both your own,
and those defined by Tapestry). You will want to narrow down which services are
actually targeted in most cases.</p><p>Note that some services, especially
those built into Tapestry IoC, are marked as <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/PreventServiceDecoration.html">not
subject to decoration</a>, this applies to service advice as well as service
decoration.</p><p>The <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/MethodAdvice.html">MethodAdvice</a>
interface is very simple; it receives an <a class="external-link"
href="http://tapestr
y.apache.org/current/apidocs/org/apache/tapestry5/ioc/Invocation.html">Invocation</a>
representing a method call. Invocation has methods for inspecting the type and
value of the parameters, and for overriding the values of the
parameters.</p><p>The call to <code>proceed()</code> allows the invocation to
continue; that is, the original method is invoked. If the method has been
advised multiple times, the call to proceed() may chain into the next
MethodAdvice object. In any case, after invoking <code>proceed()</code>, you
may inspect and override the result (the return value).</p><p>Advice is pretty
efficient, but it is still better to apply it only to methods that make sense.
We can improve the service advisor method in our example to only advise methods
that return String:</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;"> @Match("*")
public static void adviseNonNull(MethodAdviceReceiver receiver)
{
MethodAdvice advice = new MethodAdvice()
@@ -103,41 +114,52 @@
receiver.adviseMethod(m, advice);
}
};
-</plain-text-body><h1 id="ServiceAdvisors-Built-inAdvice">Built-in
Advice</h1><p>Tapestry includes two built-in advisor services.</p><h2
id="ServiceAdvisors-LoggingAdvice">Logging Advice</h2><p>Logging advice is
built into Tapestry. You can apply logging advice to your services very
easily:</p><plain-text-body> @Match("*")
+</pre>
+</div></div><h1 id="ServiceAdvisors-Built-inAdvice">Built-in
Advice</h1><p>Tapestry includes two built-in advisor services.</p><h2
id="ServiceAdvisors-LoggingAdvice">Logging Advice</h2><p>Logging advice is
built into Tapestry. You can apply logging advice to your services very
easily:</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;"> @Match("*")
public static void adviseLogging(LoggingAdvisor loggingAdvisor, Logger
logger, MethodAdviceReceiver receiver)
{
loggingAdvisor.addLoggingAdvice(logger, receiver);
}
-</plain-text-body><p><a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/LoggingAdvisor.html">LoggingAdvisor</a>
is a built-in Tapestry IoC service. This demonstrates how services can be
injected into service advisor methods. The Logger parameter is the logger for
the service being advised.</p><h2 id="ServiceAdvisors-LazyAdvice">Lazy
Advice</h2><p><a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/LazyAdvisor.html">LazyAdvisor</a>
makes method invocations lazy: methods that return an interface (rather than a
value) will not execute immediately; instead, the method invocation is
postponed until a method of the return value is invoked.</p><h1
id="ServiceAdvisors-MatchingAndOrdering">Matching And Ordering</h1><p>Each
service advice method gets a unique id, obtained by stripping the "advise"
prefix from the method name. Advice ids must be unique across all
modules.</p><p>If
the @Match annotation is omitted, the advice will match against a service
with the same id.</p><p>In many cases, the order in which the advice is given
is very important; for example, you may want logging first, then transaction
management, then security checks. The @<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Order.html">Order</a>
annotation allows you to explicitly set the order.</p><h1
id="ServiceAdvisors-Annotationdrivenadvisors">Annotation driven advisors</h1><p>
+</pre>
+</div></div><p><a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/LoggingAdvisor.html">LoggingAdvisor</a>
is a built-in Tapestry IoC service. This demonstrates how services can be
injected into service advisor methods. The Logger parameter is the logger for
the service being advised.</p><h2 id="ServiceAdvisors-LazyAdvice">Lazy
Advice</h2><p><a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/LazyAdvisor.html">LazyAdvisor</a>
makes method invocations lazy: methods that return an interface (rather than a
value) will not execute immediately; instead, the method invocation is
postponed until a method of the return value is invoked.</p><h1
id="ServiceAdvisors-MatchingAndOrdering">Matching And Ordering</h1><p>Each
service advice method gets a unique id, obtained by stripping the "advise"
prefix from the method name. Advice ids must be unique across all
modules.</p><p>If the @
Match annotation is omitted, the advice will match against a service with the
same id.</p><p>In many cases, the order in which the advice is given is very
important; for example, you may want logging first, then transaction
management, then security checks. The @<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Order.html">Order</a>
annotation allows you to explicitly set the order.</p><h1
id="ServiceAdvisors-Annotationdrivenadvisors">Annotation driven advisors</h1><p>
</p><div class="confluence-information-macro
confluence-information-macro-information"><p class="title">Added in
5.2</p><span class="aui-icon aui-icon-small aui-iconfont-info
confluence-information-macro-icon"></span><div
class="confluence-information-macro-body">
</div></div>
<div class="error"><span class="error">Unknown macro: {div}</span>
-<p> </p></div>Starting from version 5.2, Tapestry supports
annotation-driven advise methods. If the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Advise.html">@Advise</a>
annotation is present, the advise method can be arbitrary named, as shown in
the following example.<plain-text-body> @Advise
+<p> </p></div>Starting from version 5.2, Tapestry supports
annotation-driven advise methods. If the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Advise.html">@Advise</a>
annotation is present, the advise method can be arbitrary named, as shown in
the following example.<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;"> @Advise
@Match("*DAO")
public static void byServiceId(MethodAdviceReceiver receiver)
{
...
}
-</plain-text-body><p>The advice above is applied to any service whose id
matches the "*DAO" pattern.</p><p>Alternatively, marker annotations can be
placed on the advise method to match a specific service.</p><plain-text-body>
@Advise
+</pre>
+</div></div><p>The advice above is applied to any service whose id matches the
"*DAO" pattern.</p><p>Alternatively, marker annotations can be placed on the
advise method to match a specific service.</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;"> @Advise
@Blue
public static void byMarkerAnnotation(MethodAdviceReceiver receiver)
{
...
}
-</plain-text-body><p>The advice above is applied to any service that is marked
by the @Blue annotation.</p><p>By default, <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Advise.html">@Advise</a>
annotation applies the advice to any service matched by the <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Match.html">@Match</a>
or marker annotations. You can limit the matching to a single service
interface, as shown in the following example.</p><plain-text-body>
@Advise(serviceInterface=MyService.class)
+</pre>
+</div></div><p>The advice above is applied to any service that is marked by
the @Blue annotation.</p><p>By default, <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Advise.html">@Advise</a>
annotation applies the advice to any service matched by the <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Match.html">@Match</a>
or marker annotations. You can limit the matching to a single service
interface, as shown in the following example.</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;"> @Advise(serviceInterface=MyService.class)
@Match("*DAO")
public static void byMatchAnnotation(MethodAdviceReceiver receiver)
{
...
}
-</plain-text-body><p>In the example above, the advice is applied to any
implementation of MyService interfaces whose id matches the "*DAO"
pattern.</p><plain-text-body> @Advise(serviceInterface=MyService.class)
+</pre>
+</div></div><p>In the example above, the advice is applied to any
implementation of MyService interfaces whose id matches the "*DAO"
pattern.</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;"> @Advise(serviceInterface=MyService.class)
@Blue
public static void byMarkerAnnotation(MethodAdviceReceiver receiver)
{
...
}
-</plain-text-body><p>The advice above is applied to any implementation of the
MyService interface that is marked by the @Blue annotation.</p><h1
id="ServiceAdvisors-DecoratorsandAdvice">Decorators and Advice</h1><p><a
href="tapestry-ioc-decorators.html">Service decorators</a> are another way to
achieve the same thing; service advisors are a more recent addition, added in
Tapestry 5.1.</p><p>It is not recommended that you mix advice and decoration.
If you do, decoration take precedence; all decorators will be in effect before
any advice (internally, they are two separate steps, with advice being
processed and the result of that used by the
decorators).</p><p> </p><p></p></div>
+</pre>
+</div></div><p>The advice above is applied to any implementation of the
MyService interface that is marked by the @Blue annotation.</p><h1
id="ServiceAdvisors-DecoratorsandAdvice">Decorators and Advice</h1><p><a
href="tapestry-ioc-decorators.html">Service decorators</a> are another way to
achieve the same thing; service advisors are a more recent addition, added in
Tapestry 5.1.</p><p>It is not recommended that you mix advice and decoration.
If you do, decoration take precedence; all decorators will be in effect before
any advice (internally, they are two separate steps, with advice being
processed and the result of that used by the
decorators).</p><p> </p><p></p></div>
</div>
<div class="clearer"></div>
Modified:
websites/production/tapestry/content/service-implementation-reloading.html
==============================================================================
--- websites/production/tapestry/content/service-implementation-reloading.html
(original)
+++ websites/production/tapestry/content/service-implementation-reloading.html
Wed Sep 20 12:29:16 2017
@@ -72,7 +72,40 @@
</p><div class="confluence-information-macro
confluence-information-macro-information"><p class="title">Added in
5.2</p><span class="aui-icon aui-icon-small aui-iconfont-info
confluence-information-macro-icon"></span><div
class="confluence-information-macro-body">
</div></div>
<div class="error"><span class="error">Unknown macro: {div}</span>
-<p> </p></div><strong>Service implementation reloading</strong> is the
live reloading of Tapestry-IOC service implementation classes without having to
stop & restart the servlet container. Available for Tapestry 5.2 and later,
it extends the developer productivity gains of Tapestry's <a
href="class-reloading.html">Live Class Reloading</a> to your Tapestry-IOC
service modules.<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 = "class-reloading" and
space = currentSpace()</parameter></rich-text-body><p>Why is this
important?</p><p>On the one hand, a good application design keeps the page and
component classes "thin" and moves logic into the services layer, for easier
reuse acros
s pages. On the other hand, moving logic into services would be less agile if
those services didn't auto-reload the way Tapestry pages do.</p><p>There are
several restrictions on this, and a couple of <a class="external-link"
href="http://en.wikipedia.org/wiki/Leaky_abstraction" rel="nofollow">leaky
abstractions</a>, but on the whole it's quite serviceable.</p><p>As of release
5.2, you can change your service implementation, and Tapestry picks up the
change immediately. A service can even change its dependencies when being
reloaded ... but it can't change its interface.</p><h2
id="ServiceImplementationReloading-Limitations">Limitations</h2><ul><li>Reloading
only works for services, and only for services with the default service scope
(i.e., global singletons). Live reloading does not apply to
<strong>modules</strong>, <strong>service interfaces</strong>, contributions,
or anything but just the service implementation.</li><li>Reloading is limited
to services that can be proxied: ser
vices with an interface and an implementation of that
interface.</li><li>Reloading requires that the underlying class files be
simple, local, filesystem files (not, for example, files inside a
JAR).</li><li>If a service has internal state of any kind, that state is lost
when the class is reloaded and the service re-instantiated. However, if a
service has a configuration, the configuration will be
<strong>reconstructed</strong> and injected into the service.</li><li>Services
are decorated only once, so any decorations or advice applies to the initially
loaded version of the class, and will not be recalculated when the class
changes.</li></ul><h2
id="ServiceImplementationReloading-ClassLoaderIssues">Class Loader
Issues</h2><p>Tapestry creates a new class loader for <em>each service
implementation</em>. When the underlying .class file changes, the class loader
is discarded along with the instance, and a new class loader is
created.</p><p>The class loader only loads the service implemen
tation class, and any inner classes for the service implementation. All other
classes are loaded by the standard class loader for the
application.</p><p>Because of how class loaders work, the class will no longer
be able to access package private classes and members of other classes in the
same package. You may see some odd IllegalAccessErrors and need to change the
visibility of package-private classes to be public.</p><p>The JVM should be
able to eventually garbage collect the class loader. However, if the class
publishes itself to some other service (for example, adding itself as a
listener to an event published by some other service), then the instance and
the garbage collector will be leaked.</p><rich-text-body><p>Be careful about
publishing any instances of a reloadable class.</p></rich-text-body><h2
id="ServiceImplementationReloading-UpdateChecks">Update Checks</h2><p>Update
checks are normally driven by tapestry-core, which periodically checks for
changed templates, message
catalogs, and component classes. Checks for changed service implementation
classes occur at the same time.</p><p>In an application that is not driven by
the web tier, you will need to periodically invoke the
<code>fireCheckForUpdates()</code> method of the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/UpdateListenerHub.html">UpdateListenerHub</a>
service (which was moved from tapestry-core to tapestry-ioc for this
purpose).</p><p> </p><p></p><p> </p></div>
+<p> </p></div><strong>Service implementation reloading</strong> is the
live reloading of Tapestry-IOC service implementation classes without having to
stop & restart the servlet container. Available for Tapestry 5.2 and later,
it extends the developer productivity gains of Tapestry's <a
href="class-reloading.html">Live Class Reloading</a> to your Tapestry-IOC
service modules.<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="service-implementation-reloading.html">Service Implementation
Reloading</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="class-reloading.html">Class Reloading</a>
+
+
+ </div>
+ </li></ul>
+</div>
+
+
+<p>Why is this important?</p><p>On the one hand, a good application design
keeps the page and component classes "thin" and moves logic into the services
layer, for easier reuse across pages. On the other hand, moving logic into
services would be less agile if those services didn't auto-reload the way
Tapestry pages do.</p><p>There are several restrictions on this, and a couple
of <a class="external-link"
href="http://en.wikipedia.org/wiki/Leaky_abstraction" rel="nofollow">leaky
abstractions</a>, but on the whole it's quite serviceable.</p><p>As of release
5.2, you can change your service implementation, and Tapestry picks up the
change immediately. A service can even change its dependencies when being
reloaded ... but it can't change its interface.</p><h2
id="ServiceImplementationReloading-Limitations">Limitations</h2><ul><li>Reloading
only works for services, and only for services with the default service scope
(i.e., global singletons). Live reloading does not apply to <strong>mo
dules</strong>, <strong>service interfaces</strong>, contributions, or
anything but just the service implementation.</li><li>Reloading is limited to
services that can be proxied: services with an interface and an implementation
of that interface.</li><li>Reloading requires that the underlying class files
be simple, local, filesystem files (not, for example, files inside a
JAR).</li><li>If a service has internal state of any kind, that state is lost
when the class is reloaded and the service re-instantiated. However, if a
service has a configuration, the configuration will be
<strong>reconstructed</strong> and injected into the service.</li><li>Services
are decorated only once, so any decorations or advice applies to the initially
loaded version of the class, and will not be recalculated when the class
changes.</li></ul><h2
id="ServiceImplementationReloading-ClassLoaderIssues">Class Loader
Issues</h2><p>Tapestry creates a new class loader for <em>each service
implementation</em>. Whe
n the underlying .class file changes, the class loader is discarded along with
the instance, and a new class loader is created.</p><p>The class loader only
loads the service implementation class, and any inner classes for the service
implementation. All other classes are loaded by the standard class loader for
the application.</p><p>Because of how class loaders work, the class will no
longer be able to access package private classes and members of other classes
in the same package. You may see some odd IllegalAccessErrors and need to
change the visibility of package-private classes to be public.</p><p>The JVM
should be able to eventually garbage collect the class loader. However, if the
class publishes itself to some other service (for example, adding itself as a
listener to an event published by some other service), then the instance and
the garbage collector will be leaked.</p><div
class="confluence-information-macro confluence-information-macro-note"><span
class="aui-icon aui-ico
n-small aui-iconfont-warning confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>Be careful about publishing any
instances of a reloadable class.</p></div></div><h2
id="ServiceImplementationReloading-UpdateChecks">Update Checks</h2><p>Update
checks are normally driven by tapestry-core, which periodically checks for
changed templates, message catalogs, and component classes. Checks for changed
service implementation classes occur at the same time.</p><p>In an application
that is not driven by the web tier, you will need to periodically invoke the
<code>fireCheckForUpdates()</code> method of the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/UpdateListenerHub.html">UpdateListenerHub</a>
service (which was moved from tapestry-core to tapestry-ioc for this
purpose).</p><p> </p><p></p><p> </p></div>
</div>
<div class="clearer"></div>
Modified: websites/production/tapestry/content/service-serialization.html
==============================================================================
--- websites/production/tapestry/content/service-serialization.html (original)
+++ websites/production/tapestry/content/service-serialization.html Wed Sep 20
12:29:16 2017
@@ -27,6 +27,14 @@
</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>
+ SyntaxHighlighter.defaults['toolbar'] = false;
+ SyntaxHighlighter.all();
+ </script>
<link href="/styles/style.css" rel="stylesheet" type="text/css"/>
@@ -36,26 +44,13 @@
<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">Service Serialization</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">Service Serialization</h1></div></div>
<div class="clearer"></div>
</div>
@@ -67,9 +62,11 @@
</div>
<div id="content">
- <div id="ConfluenceContent"><p><strong>Service
serialization</strong> is the serialization of a Tapestry IOC
service.</p><p>Every once in a while you may need to serialize a service. For
example, you may store an object into the HttpSession that holds a reference to
a service. In a clustered environment, that object will be serialized and
broadcast to other servers in the cluster.</p><p>Services in Tapestry are
serializable. Specifically, service <em>proxies</em> are serializable. However,
your service implementations <em>do not</em> have to be
serializable.</p><p>Serialization works as follows:</p><ul><li>When a proxy is
serialized, it instead serializes a <em>token</em> object.</li><li>The token
object is what's stored in the output stream.</li><li>When the token is
de-serialized, it locates the service proxy in the current Registry and returns
that.</li></ul><p>The end result is very efficient: just the tiny tokens are
serialized, not the services with their proxi
es, configurations, implementations, dependencies, internal state and so
forth.</p><p>Again, note that the actual service implementation is not
serialized. Due to Tapestry's lazy creation policy, the service implementation
may not even exist. Since outside code only sees the proxy, there's no
difference.</p><h1 id="ServiceSerialization-RegistryResolution">Registry
Resolution</h1><p>The one trick here is locating the service proxy. Tapestry
uses a <em>weak reference</em> to the Registry to do this. When a Registry
starts up, it is stored in the reference, so that de-serialization can
work.</p><p>The reference is cleared when you shut down the Registry. If you
stop using the Registry, but fail to shut it down, the weak reference ensures
that it will be released to the garbage collector anyway. Still, you should
shutdown a Registry when done with it.</p><p>This all makes one BIG assumption:
that there's just one Registry. That's normal for a web application, especially
when the tapestr
y-ioc JAR is included as part of the web application's WAR.</p><p>If you are
running multiple Registries you will likely see errors in your
console:</p><plain-text-body>[ERROR] SerializationSupport Setting a new service
proxy provider when there's already
+ <div id="ConfluenceContent"><p><strong>Service
serialization</strong> is the serialization of a Tapestry IOC
service.</p><p>Every once in a while you may need to serialize a service. For
example, you may store an object into the HttpSession that holds a reference to
a service. In a clustered environment, that object will be serialized and
broadcast to other servers in the cluster.</p><p>Services in Tapestry are
serializable. Specifically, service <em>proxies</em> are serializable. However,
your service implementations <em>do not</em> have to be
serializable.</p><p>Serialization works as follows:</p><ul><li>When a proxy is
serialized, it instead serializes a <em>token</em> object.</li><li>The token
object is what's stored in the output stream.</li><li>When the token is
de-serialized, it locates the service proxy in the current Registry and returns
that.</li></ul><p>The end result is very efficient: just the tiny tokens are
serialized, not the services with their proxi
es, configurations, implementations, dependencies, internal state and so
forth.</p><p>Again, note that the actual service implementation is not
serialized. Due to Tapestry's lazy creation policy, the service implementation
may not even exist. Since outside code only sees the proxy, there's no
difference.</p><h1 id="ServiceSerialization-RegistryResolution">Registry
Resolution</h1><p>The one trick here is locating the service proxy. Tapestry
uses a <em>weak reference</em> to the Registry to do this. When a Registry
starts up, it is stored in the reference, so that de-serialization can
work.</p><p>The reference is cleared when you shut down the Registry. If you
stop using the Registry, but fail to shut it down, the weak reference ensures
that it will be released to the garbage collector anyway. Still, you should
shutdown a Registry when done with it.</p><p>This all makes one BIG assumption:
that there's just one Registry. That's normal for a web application, especially
when the tapestr
y-ioc JAR is included as part of the web application's WAR.</p><p>If you are
running multiple Registries you will likely see errors in your console:</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;">[ERROR] SerializationSupport Setting a new service
proxy provider when there's already
an existing provider. This may indicate that you have multiple IoC Registries.
-</plain-text-body><p> </p><p></p></div>
+</pre>
+</div></div><p> </p><p></p></div>
</div>
<div class="clearer"></div>
Modified: websites/production/tapestry/content/session-storage.html
==============================================================================
--- websites/production/tapestry/content/session-storage.html (original)
+++ websites/production/tapestry/content/session-storage.html Wed Sep 20
12:29:16 2017
@@ -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,28 +77,92 @@
</div>
<div id="content">
- <div id="ConfluenceContent"><p> </p><p>Most web
applications will need to have some data that is shared across multiple pages.
Perhaps you are creating a multi-page wizard, or you have an object that tracks
the user's identify once logged in, or maybe you need to manage a shopping
cart.</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>Ordinary <a
href="persistent-page-data.html">page-persistent fields</a> won't work for
this, since persistent fields are available only to a specific page, not shared
across multiple pages.</p><p>Tapestry provides two mechanisms for storing such
data: Sessio
n State Objects and Session Attributes. When deciding between the two, it's
best to use Session State Objects for complex objects, and Session Attributes
for simple types.</p><h2 id="SessionStorage-SessionStateObjects">Session State
Objects</h2><p>With a Session State Object (SSO), the value is automatically
stored outside the page; with the default storage strategy, it is stored in the
session. Such a value is global to all pages <em>for the same user</em>, but is
stored separately for different users.</p><p>A field holding an SSO is marked
with the @<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/SessionState.html">SessionState</a>
annotation.<plain-text-body>{float:right}
-{panel:background=#eee|title=Contents}
-{toc:minLevel=2|maxLevel=4}
-{panel}
-{float}</plain-text-body>Example:</p><parameter
ac:name="">java</parameter><plain-text-body>public class MyPage
+ <div id="ConfluenceContent"><p> </p><p>Most web
applications will need to have some data that is shared across multiple pages.
Perhaps you are creating a multi-page wizard, or you have an object that tracks
the user's identify once logged in, or maybe you need to manage a shopping
cart.</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>Ordinary <a href="persistent-page-data.html">page-persistent fields</a>
won't work for this, since persistent fields are available only to a specific
page, not shared across multiple pages.</p><p>Tapestry provides two mechanisms
for storing such data: Session State Objects and Session Attributes. When
deciding between the two, it's best to use Session State Objects for complex
objects, and Session Attributes for simple types.</p><h2
id="SessionStorage-SessionStateObjects">Session State Objects</h2><p>With a
Session State Object (SSO), the value is automatically stored outside the page;
with the default storage strategy, it is stored in the session. Such a value is
global to all pages <em>for the same user</em>, but is stored separately for
different users.</p><p>A field holding an SSO is marked with the @<a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/SessionState.html">SessionState</a>
annotation.</p><div class="navmen
u" style="float:right; background:white; margin:3px; padding:3px">
+<div class="panel" style="border-width: 1px;"><div class="panelHeader"
style="border-bottom-width: 1px;"><b>Contents</b></div><div
class="panelContent">
+<style type="text/css">/*<![CDATA[*/
+div.rbtoc1499639543191 {padding: 0px;}
+div.rbtoc1499639543191 ul {list-style: disc;margin-left: 0px;}
+div.rbtoc1499639543191 li {margin-left: 0px;padding-left: 0px;}
+
+/*]]>*/</style><div class="toc-macro rbtoc1499639543191">
+<ul class="toc-indentation"><li>Related Articles</li></ul>
+<ul><li><a href="#SessionStorage-SessionStateObjects">Session State
Objects</a>
+<ul class="toc-indentation"><li><a
href="#SessionStorage-Pitfalls">Pitfalls</a></li><li><a
href="#SessionStorage-CheckforCreation">Check for Creation</a></li><li><a
href="#SessionStorage-PersistenceStrategies">Persistence
Strategies</a></li><li><a href="#SessionStorage-ConfiguringSSOs">Configuring
SSOs</a></li></ul>
+</li><li><a href="#SessionStorage-SessionAttributes">Session Attributes</a>
+<ul class="toc-indentation"><li><a
href="#SessionStorage-Pitfalls.1">Pitfalls</a></li></ul>
+</li><li><a href="#SessionStorage-ClusteringIssues">Clustering Issues</a>
+<ul class="toc-indentation"><li><a
href="#SessionStorage-@ImmutableSessionPersistedObjectAnnotation">@ImmutableSessionPersistedObject
Annotation</a></li><li><a
href="#SessionStorage-OptimizedSessionPersistedObjectInterface">OptimizedSessionPersistedObject
Interface</a></li><li><a
href="#SessionStorage-SessionPersistedObjectAnalyzerService">SessionPersistedObjectAnalyzer
Service</a></li></ul>
+</li><li><a href="#SessionStorage-SessionLocking">Session
Locking</a></li></ul>
+</div>
+</div></div></div>Example:<div class="code panel pdl" style="border-width:
1px;"><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public class MyPage
{
@SessionState
private ShoppingCart shoppingCart;
. . .
}
-</plain-text-body><p>Any other component or page that declares a field
<strong>of the same type</strong>, regardless of name, and marks it with the
SessionState annotation will share the same value. It's that simple. However,
using @SessionState <em>safely</em> requires care:</p><rich-text-body><p>DO NOT
USE @SessionState FOR SIMPLE TYPES! Only use it on variables that are of a
custom-built class designed expressly for this purpose! <strong>See the
Pitfalls section below</strong>.</p></rich-text-body><p>The first time you
access an SSO, it is created automatically. Typically, the SSO will have a
public no-args constructor ... but you may inject dependencies into the SSO via
its constructor, as you can with a Tapestry IoC service
implementation.</p><p><em>For Tapestry 4 Users:</em> a big change here is that
you don't need to provide any configuration for the SSO before using it, nor do
you provide a logical name. Tapestry 5 uses the class name to identify the SSO,
so there's no need
for a logical name.</p><p>Assigning a value to an SSO field will store that
value. Assigning null to an SSO field will remove the SSO (reading the field
subsequently will force a new SSO instance to be created).</p><h3
id="SessionStorage-Pitfalls">Pitfalls</h3><p>With @SessionState, you are
creating a session-wide data storage area that is tied to the <em>type</em>
(class) of the variable you annotate. It is not specifically tied to the
variable itself, or even to the class in which that variable was annotated. As
with all session data, there is the serious possibility of collisions, not just
within your application but with other modules/libraries:</p><parameter
ac:name="title">Example of Data Collision -- Don't Do
This!</parameter><plain-text-body> @SessionState
+</pre>
+</div></div><p>Any other component or page that declares a field <strong>of
the same type</strong>, regardless of name, and marks it with the SessionState
annotation will share the same value. It's that simple. However, using
@SessionState <em>safely</em> requires care:</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>DO NOT USE @SessionState FOR
SIMPLE TYPES! Only use it on variables that are of a custom-built class
designed expressly for this purpose! <strong>See the Pitfalls section
below</strong>.</p></div></div><p>The first time you access an SSO, it is
created automatically. Typically, the SSO will have a public no-args
constructor ... but you may inject dependencies into the SSO via its
constructor, as you can with a Tapestry IoC service
implementation.</p><p><em>For Tapestry 4 Users:</em> a big ch
ange here is that you don't need to provide any configuration for the SSO
before using it, nor do you provide a logical name. Tapestry 5 uses the class
name to identify the SSO, so there's no need for a logical
name.</p><p>Assigning a value to an SSO field will store that value. Assigning
null to an SSO field will remove the SSO (reading the field subsequently will
force a new SSO instance to be created).</p><h3
id="SessionStorage-Pitfalls">Pitfalls</h3><p>With @SessionState, you are
creating a session-wide data storage area that is tied to the <em>type</em>
(class) of the variable you annotate. It is not specifically tied to the
variable itself, or even to the class in which that variable was annotated. As
with all session data, there is the serious possibility of collisions, not just
within your application but with other modules/libraries:</p><div class="code
panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>Example
of Data Collision – Don't Do This!</b></div><div class="codeContent
panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @SessionState
private String userName; // Unsafe -- String is not a custom type
... then, later in this class or any other:
@sessionState
private String userCity; // This overwrites value in userName, because
it's also a String!
-</plain-text-body><p>The simple rule is, NEVER use @SessionState for
simple-type variables. It is ALWAYS worth taking the time to build a special
class to hold your session state information. Doing so will force you to
consolidate that information into a single, logical unit that can't be
accidentally accessed by other classes. (Alternatively, see the Session
Attribute section below.)</p><h3 id="SessionStorage-CheckforCreation">Check for
Creation</h3><p>Scalable web applications do not create the server-side session
needlessly. If you can avoid creating the session, especially on first access
to your web application, you will be able to handle an order of magnitude more
users. So, if you can avoid creating the SSO, you should do so.</p><p>But how
to avoid creating it? Simply checking ("shoppingCart!= null") will force the
creation of the SSO and the session to store it in.</p><p>Instead, create a
second field with a matching name but with "Exists" appended:</p><parameter
ac:name="">
java</parameter><plain-text-body> private boolean shoppingCartExists;
-</plain-text-body><p>It is not annotated; it is located by naming convention
("Exists" appended). It must be type boolean and must be a private instance
variable. Tapestry will automatically set this variable to <code>true</code>
when the SSO is created, so you can check it to see if the SSO already
exists.</p><p>Alternately, you may allow for the state being
null:</p><parameter ac:name="">java</parameter><plain-text-body>
@SessionState(create=false)
+</pre>
+</div></div><p>The simple rule is, NEVER use @SessionState for simple-type
variables. It is ALWAYS worth taking the time to build a special class to hold
your session state information. Doing so will force you to consolidate that
information into a single, logical unit that can't be accidentally accessed by
other classes. (Alternatively, see the Session Attribute section below.)</p><h3
id="SessionStorage-CheckforCreation">Check for Creation</h3><p>Scalable web
applications do not create the server-side session needlessly. If you can avoid
creating the session, especially on first access to your web application, you
will be able to handle an order of magnitude more users. So, if you can avoid
creating the SSO, you should do so.</p><p>But how to avoid creating it? Simply
checking ("shoppingCart!= null") will force the creation of the SSO and the
session to store it in.</p><p>Instead, create a second field with a matching
name but with "Exists" appended:</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;"> private boolean shoppingCartExists;
+</pre>
+</div></div><p>It is not annotated; it is located by naming convention
("Exists" appended). It must be type boolean and must be a private instance
variable. Tapestry will automatically set this variable to <code>true</code>
when the SSO is created, so you can check it to see if the SSO already
exists.</p><p>Alternately, you may allow for the state being null:</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;"> @SessionState(create=false)
private ShoppingCart shoppingCart;
-</plain-text-body><p>In this case, the shoppingCart field will be null if the
ShoppingCart SSO does not exist, but will be non-null if it has been created
(either by assigning a value to the field, or by a different SSO field where
create is true).</p><h3 id="SessionStorage-PersistenceStrategies">Persistence
Strategies</h3><p>Main Article: <a href="persistent-page-data.html">Persistent
Page Data</a></p><p>Each SSO is managed according to a persistence strategy.
The default persistence strategy, "session", stores the SSOs inside the
session. The session is created as needed.</p><h3
id="SessionStorage-ConfiguringSSOs">Configuring SSOs</h3><p>Generally, you will
need to configure your Session State Object if you want to change the
persistence strategy to other than the default. (Right now there's only one
built in strategy, but more will be coming in the future.)</p><p>Alternately,
you can configure a Session State Object in order to control how it is
instantiated. You may need to inj
ect some values into the SSO when it is first created, or otherwise initialize
it. In this case, you may provide an <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/ApplicationStateCreator.html">ApplicationStateCreator</a>
object, which will be called upon to create the SSO as necessary. This is also
the technique to use when you want your SSO to be represented by an
<em>interface</em> rather than a <em>class</em>: you need to provide a creator
that knows about the class that implements the interface.</p><p>A Session State
Object is configured using contributions to the ApplicationStateManager
service. From your application's module:</p><parameter
ac:name="">java</parameter><plain-text-body> public void
contributeApplicationStateManager(MappedConfiguration<Class,
ApplicationStateContribution> configuration)
+</pre>
+</div></div><p>In this case, the shoppingCart field will be null if the
ShoppingCart SSO does not exist, but will be non-null if it has been created
(either by assigning a value to the field, or by a different SSO field where
create is true).</p><h3 id="SessionStorage-PersistenceStrategies">Persistence
Strategies</h3><p>Main Article: <a href="persistent-page-data.html">Persistent
Page Data</a></p><p>Each SSO is managed according to a persistence strategy.
The default persistence strategy, "session", stores the SSOs inside the
session. The session is created as needed.</p><h3
id="SessionStorage-ConfiguringSSOs">Configuring SSOs</h3><p>Generally, you will
need to configure your Session State Object if you want to change the
persistence strategy to other than the default. (Right now there's only one
built in strategy, but more will be coming in the future.)</p><p>Alternately,
you can configure a Session State Object in order to control how it is
instantiated. You may need to inject so
me values into the SSO when it is first created, or otherwise initialize it.
In this case, you may provide an <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/ApplicationStateCreator.html">ApplicationStateCreator</a>
object, which will be called upon to create the SSO as necessary. This is also
the technique to use when you want your SSO to be represented by an
<em>interface</em> rather than a <em>class</em>: you need to provide a creator
that knows about the class that implements the interface.</p><p>A Session State
Object is configured using contributions to the ApplicationStateManager
service. From your application's module:</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> public void
contributeApplicationStateManager(MappedConfiguration<Class,
ApplicationStateContribution> configuration)
{
ApplicationStateCreator<MyState> creator = new
ApplicationStateCreator<ShoppingCart>()
{
@@ -100,12 +174,14 @@
configuration.add(ShoppingCart.class, new
ApplicationStateContribution("session", creator));
}
-</plain-text-body><p>Here, we have an SSO type of ShoppingCart, and we're
providing a creator for it. We've dolled the creator up with some generic
types, but that isn't essential.</p><p>Our creator creates a new MyState
instance using an alternate constructor that takes the current date and time.
Again, just an example.</p><p>Finally, we create an <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/ApplicationStateContribution.html">ApplicationStateContribution</a>
identifying the strategy name and the creator, and give that to the
configuration.</p><p><strong>Note:</strong> You might be confused by the name
"_Application_StateManager" and "_Application_StateCreator"; these reflect a
difference in naming between 5.0 and 5.1; SSOs were originally called
"Application State Objects", but that naming implied they were stored in the
ServletContext, as application global to all users. The new SessionState
annotation was introduced, but
the existing services need to keep thier names as-is.</p><h2
id="SessionStorage-SessionAttributes">Session Attributes</h2>
+</pre>
+</div></div><p>Here, we have an SSO type of ShoppingCart, and we're providing
a creator for it. We've dolled the creator up with some generic types, but that
isn't essential.</p><p>Our creator creates a new MyState instance using an
alternate constructor that takes the current date and time. Again, just an
example.</p><p>Finally, we create an <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/ApplicationStateContribution.html">ApplicationStateContribution</a>
identifying the strategy name and the creator, and give that to the
configuration.</p><p><strong>Note:</strong> You might be confused by the name
"_Application_StateManager" and "_Application_StateCreator"; these reflect a
difference in naming between 5.0 and 5.1; SSOs were originally called
"Application State Objects", but that naming implied they were stored in the
ServletContext, as application global to all users. The new SessionState
annotation was introduced, but the e
xisting services need to keep thier names as-is.</p><h2
id="SessionStorage-SessionAttributes">Session Attributes</h2>
<div class="confluence-information-macro
confluence-information-macro-information"><p class="title">Added in
5.2</p><span class="aui-icon aui-icon-small aui-iconfont-info
confluence-information-macro-icon"></span><div
class="confluence-information-macro-body">
</div></div>
<div class="error"><span class="error">Unknown macro: {div}</span>
-<p> </p></div><p>As an alternative to SSOs, Tapestry provides a
<strong>Session Attribute</strong> mechanism, which lets you store data in the
session by name (rather than type). It is particularly useful when integrating
Tapestry with legacy applications that directly manipulate the
HttpSession.</p><parameter ac:name="title">The Old
Way</parameter><plain-text-body>public class Page {
+<p> </p></div><p>As an alternative to SSOs, Tapestry provides a
<strong>Session Attribute</strong> mechanism, which lets you store data in the
session by name (rather than type). It is particularly useful when integrating
Tapestry with legacy applications that directly manipulate the
HttpSession.</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>The Old
Way</b></div><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public class Page {
@Inject
private Request request;
@@ -113,15 +189,21 @@
return (User)
request.getSession(true).getAttribute("loggedInUserName");
}
}
-</plain-text-body><p>Starting with Tapestry 5.2, this can be accomplished just
by annotating a page or component property with @<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/SessionAttribute.html">SessionAttribute</a>.
This annotation is used to map a property of a page or component to value
stored in session. Unlike Session State Objects, the name (not the type) of the
annotated property is used as the name of the session attribute to look
for.</p><parameter ac:name="title">The New
Way</parameter><plain-text-body>public class Page {
+</pre>
+</div></div><p>Starting with Tapestry 5.2, this can be accomplished just by
annotating a page or component property with @<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/SessionAttribute.html">SessionAttribute</a>.
This annotation is used to map a property of a page or component to value
stored in session. Unlike Session State Objects, the name (not the type) of the
annotated property is used as the name of the session attribute to look
for.</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>The New
Way</b></div><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public class Page {
@SessionAttribute
private User loggedInUserName;
}
-</plain-text-body><p>You can also provide a name using the annotation's
<code>value</code> parameter:</p><plain-text-body>public class Page {
+</pre>
+</div></div><p>You can also provide a name using the annotation's
<code>value</code> parameter:</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public class Page {
@SessionAttribute("loggedInUserName")
private User userName;
}
-</plain-text-body><h3 id="SessionStorage-Pitfalls.1">Pitfalls</h3><p>As with
SSOs, when using Session Attributes you are creating a session-wide data
storage area that has the serious possibility of data collisions, not just
within your application but with other modules/libraries. To avoid problems,
you should qualify the session attribute name with a package-like naming
convention. For example, use something like "com.mycompany.myapp.username"
instead of just "username".</p><p>It's best to define the session attribute
name as constant, and use that in the annotation's value parameter, rather then
defaulting to the instance variable name. This will help prevent subtle runtime
errors due to misspellings. For example:</p><parameter ac:name="title">The
Safer Way</parameter><plain-text-body>public static final String
USER_NAME_SESSION_ATTRIBUTE = "com.example.shoppingapp.username";
+</pre>
+</div></div><h3 id="SessionStorage-Pitfalls.1">Pitfalls</h3><p>As with SSOs,
when using Session Attributes you are creating a session-wide data storage area
that has the serious possibility of data collisions, not just within your
application but with other modules/libraries. To avoid problems, you should
qualify the session attribute name with a package-like naming convention. For
example, use something like "com.mycompany.myapp.username" instead of just
"username".</p><p>It's best to define the session attribute name as constant,
and use that in the annotation's value parameter, rather then defaulting to the
instance variable name. This will help prevent subtle runtime errors due to
misspellings. For example:</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>The Safer Way</b></div><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public static final String USER_NAME_SESSION_ATTRIBUTE
= "com.example.shoppingapp.username";
...
@@ -129,12 +211,41 @@ public class Page {
@SessionAttribute(USER_NAME_SESSION_ATTRIBUTE)
private User userName;
}
-</plain-text-body><p><parameter ac:name=""><a
href="clustering-issues.html">Clustering Issues</a></parameter></p><h2
id="SessionStorage-SessionLocking">Session Locking</h2><p>Starting with version
5.4, by default Tapestry will apply locking semantics around access to the
HttpSession. Reading attribute names occurs with a shared read lock, and
getting or setting an attribute upgrades the lock to an exclusive write lock.
This can tend to serialize threads when a number of simultaneous (Ajax)
requests from the client arrive. However, many implementations of HttpSession
are not thread safe, and often mutable objects<br clear="none"> are stored in
the session and shared between threads.</p><p>The
<code>tapestry.session-locking-enabled</code> <a
href="configuration.html">symbol</a> can control this behavior. Setting this to
true (the default) will yield a more robust application; setting it to false
may speed up processing for more Ajax intensive applications (but care should
then be gi
ven to ensuring that objects shared inside the session are themeselves
immutable or thread-safe).</p><parameter ac:name="title">AppModule.java
(partial)</parameter><plain-text-body> public static void
contributeApplicationDefaults(MappedConfiguration<String,String>
configuration)
+</pre>
+</div></div><p></p><h2 id="SessionStorage-ClusteringIssues">Clustering
Issues</h2>
+
+<p>The Servlet API was designed with the intention that there would be only a
modest amount of server-side state, and that the stored values would be
individual numbers and strings, and thus, immutable.</p>
+
+<p>However, many web applications do not use the HttpSession this way, instead
storing large, mutable objects in the session. This is not a problem for single
servers, but in a cluster, anything stored in the session must be serialized to
a bytestream and distributed to other servers within the cluster, and restored
there.</p>
+
+<p>Most application servers perform that serialization and distribution
whenever HttpSession.setAttribute() is called. This creates a data consistency
problem for mutable objects, because if you read a mutable session object,
change its state, but <em>don't</em> invoke setAttribute(), the changes will be
isolated to just a single server in the cluster.</p>
+
+<p>Tapestry attempts to solve this: any session-persisted object that is read
during a request will be re-stored back into the HttpSession at the end of the
request. This ensures that changed internal state of those mutable objects is
properly replicated around the cluster.</p>
+
+<p>But while this solution solves the data consistency problem, it does so at
the expense of performance, since all of those calls to setAttribute() result
in extra session data being replicated needlessly if the internal state of the
mutable object hasn't changed.</p>
+
+<p>Tapestry has solutions to this, too:</p>
+
+<h3
id="SessionStorage-@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="SessionStorage-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="SessionStorage-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><h2 id="SessionStorage-SessionLocking">Session
Locking</h2><p>Starting with version 5.4, by default Tapestry will apply
locking semantics around access to the HttpSession. Reading attribute names
occurs with a shared read lock, and getting or setting an attribute upgrades
the lock to an exclusive write lock. This can tend to serialize threads when a
number of simultaneous (Ajax) requests from the client arrive. However, many
implementations of HttpSession are not thread safe, and often mutable
objects<br clear="none"> are stored in the session and shared betwe
en threads.</p><p>The <code>tapestry.session-locking-enabled</code> <a
href="configuration.html">symbol</a> can control this behavior. Setting this to
true (the default) will yield a more robust application; setting it to false
may speed up processing for more Ajax intensive applications (but care should
then be given to ensuring that objects shared inside the session are
themeselves immutable or thread-safe).</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>AppModule.java (partial)</b></div><div
class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> public static void
contributeApplicationDefaults(MappedConfiguration<String,String>
configuration)
{
configuration.add(SymbolConstants.SESSION_LOCKING_ENABLED, true);
...
}
-</plain-text-body></div>
+</pre>
+</div></div></div>
</div>
<div class="clearer"></div>
Modified: websites/production/tapestry/content/shadowbuilder-service.html
==============================================================================
--- websites/production/tapestry/content/shadowbuilder-service.html (original)
+++ websites/production/tapestry/content/shadowbuilder-service.html Wed Sep 20
12:29:16 2017
@@ -27,6 +27,14 @@
</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>
+ SyntaxHighlighter.defaults['toolbar'] = false;
+ SyntaxHighlighter.all();
+ </script>
<link href="/styles/style.css" rel="stylesheet" type="text/css"/>
@@ -36,26 +44,13 @@
<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">ShadowBuilder Service</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">ShadowBuilder Service</h1></div></div>
<div class="clearer"></div>
</div>
@@ -67,18 +62,48 @@
</div>
<div id="content">
- <div id="ConfluenceContent"><p>The <strong>ShadowBuilder
service</strong> (see the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/PropertyShadowBuilder.html">PropertyShadowBuilder</a>
API) is used to build a special, delegating kind of service implementation
that, essentially, allows a property of another service to be exposed as its
own service.</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 = "service-builders" and
space = currentSpace()</parameter></rich-text-body><p>For example, the
tapestry-core module provides a Request property as a shadow of the
RequestGlobals service's request property:</p><para
meter ac:name="">java</parameter><plain-text-body>public Request build()
+ <div id="ConfluenceContent"><p>The <strong>ShadowBuilder
service</strong> (see the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/PropertyShadowBuilder.html">PropertyShadowBuilder</a>
API) is used to build a special, delegating kind of service implementation
that, essentially, allows a property of another service to be exposed as its
own service.</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="shadowbuilder-service.html">ShadowBuilder Service</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="strategybuilder-service.html">StrategyBuilder Service</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="pipelinebuilder-service.html">PipelineBuilder Service</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="chainbuilder-service.html">ChainBuilder Service</a>
+ </div> </li></ul></div><p>For example, the tapestry-core module provides a
Request property as a shadow of the RequestGlobals service's request
property:</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public Request build()
{
return shadowBuilder.build(requestGlobals, "request", Request.class);
-}</plain-text-body><p>(shadowBuilder and requestGlobals are injected into the
module class instance)</p><p>This can be thought of as similar
to:</p><parameter ac:name="">java</parameter><plain-text-body>public Request
build()
+}</pre>
+</div></div><p>(shadowBuilder and requestGlobals are injected into the module
class instance)</p><p>This can be thought of as similar to:</p><div class="code
panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">public Request build()
{
return requestGlobals.getRequest();
-}</plain-text-body><p>However there is a <em>critical</em> difference between
the two: a shadow property is <em>re-evaluated on each method invocation</em>.
In the former case, the Request service will always obtain the current value of
the request property from the per-thread RequestGlobals service. The second
example is more than likely broken, since it will expose whatever value is in
the request property of the RequestGlobals <em>at the time the Request service
implementation is realized</em>.</p><p>Notice that in this example, the Request
service is a normal singleton. This service can be freely injected into any
service throughout the framework or application. Invoking methods on this
service will always delegate to the current thread's request. Callers don't
have to be aware of this internal delegation; it just happens.</p><h1
id="ShadowBuilderService-Non-Reflective">Non-Reflective</h1><p>When the shadow
is created, reflection is used to translate the property name to a metho
d name. This information is used to build a new class (at runtime) that is
instantiated as the service implementation.</p><p>A typical method is
implemented as (approximately):</p><parameter
ac:name="">java</parameter><plain-text-body>private final RequestGlobals source;
+}</pre>
+</div></div><p>However there is a <em>critical</em> difference between the
two: a shadow property is <em>re-evaluated on each method invocation</em>. In
the former case, the Request service will always obtain the current value of
the request property from the per-thread RequestGlobals service. The second
example is more than likely broken, since it will expose whatever value is in
the request property of the RequestGlobals <em>at the time the Request service
implementation is realized</em>.</p><p>Notice that in this example, the Request
service is a normal singleton. This service can be freely injected into any
service throughout the framework or application. Invoking methods on this
service will always delegate to the current thread's request. Callers don't
have to be aware of this internal delegation; it just happens.</p><h1
id="ShadowBuilderService-Non-Reflective">Non-Reflective</h1><p>When the shadow
is created, reflection is used to translate the property name to a method name.
This information is used to build a new class (at runtime) that is
instantiated as the service implementation.</p><p>A typical method is
implemented as (approximately):</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;">private final RequestGlobals source;
public String getParameter(String name)
{
return source.getRequest().getParameter(name);
-}</plain-text-body><p>That is, the shadow implementation holds onto the target
object (in the above example, the RequestGlobals service) and invokes a method
on it directly, not using reflection, no differently than you would if you
wrote the code yourself.</p><p> </p><p></p></div>
+}</pre>
+</div></div><p>That is, the shadow implementation holds onto the target object
(in the above example, the RequestGlobals service) and invokes a method on it
directly, not using reflection, no differently than you would if you wrote the
code yourself.</p><p> </p><p></p></div>
</div>
<div class="clearer"></div>