You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by sk...@apache.org on 2007/10/31 16:01:27 UTC

svn commit: r590700 - /myfaces/orchestra/trunk/core/src/site/xdoc/component-bindings.xml

Author: skitching
Date: Wed Oct 31 08:01:26 2007
New Revision: 590700

URL: http://svn.apache.org/viewvc?rev=590700&view=rev
Log:
Add section about "request scoped facade" solution to component bindings.
General tidyups of documentation.

Modified:
    myfaces/orchestra/trunk/core/src/site/xdoc/component-bindings.xml

Modified: myfaces/orchestra/trunk/core/src/site/xdoc/component-bindings.xml
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/site/xdoc/component-bindings.xml?rev=590700&r1=590699&r2=590700&view=diff
==============================================================================
--- myfaces/orchestra/trunk/core/src/site/xdoc/component-bindings.xml (original)
+++ myfaces/orchestra/trunk/core/src/site/xdoc/component-bindings.xml Wed Oct 31 08:01:26 2007
@@ -28,52 +28,78 @@
 	<body>
 		<section name="Component Bindings">
 			<subsection name="Introduction">
-				<subsubsection name="Basic discrepancy">
-				While the best pratice is to have a single page controller under conversation per page or 
-				for a full multi page flow, there is a discrepancy between the scope length needed for the conversation
-				and the scope length for associated component bindings. This discrepancy is not related to Orchestra;
-                it is much more related to the JSF-specification itself.
+				<subsubsection name="Basic discrepancy">
+				<p>
+				JSF components interact with backing beans. However the lifecycles of these two types of
+				objects are very different; beans may last just a request, or longer (eg a session bean).
+				A component logically may last a number of page views, but physically new instances are
+				created for each request, when the view is "restored".
+				</p>
+				<p>
+				JSF is designed so that in normal cases views "pull" data from backing beans, "push" data entered by the
+				user into backing beans, and "invoke" event-handler methods on backing beans. In all cases, the dependency
+				is unidirectional: views know about beans, but beans do not know about views. And the coupling is done
+				only via EL strings, not hard references. This means that the lifecycles of the view components and
+				the backing beans they reference are decoupled, and the lifecycle mismatch is not an issue.
+				</p>
+				<p>
+				However JSF does provide a mechanism for backing beans to access view components in the rare cases where
+				this is necessary: "bindings". Unfortunately the coupling here is done via direct object references, not
+				EL strings. And if a bean then holds on to these references then very bad things can happen if the
+				lifetime of the bean is longer than a request. Just one of the possible problems is the ugly
+				"duplicate component id" error.
+				</p>
+				<p>
+				Note that "bindings" are not generally a good idea, and should be avoided where possible. In the
+				remainder of this page we assume that you have some very good reason for wanting bindings, and
+				show you how they can be used with non-request-scoped beans.
+				</p>
                 </subsubsection>
 				
 			</subsection>
 			<subsection name="Problem">
-				The main problem is that component bindings need to be in a request-scoped backing-bean.
-				The component tree is regenerated at every request. Longer component bindings
-				are a possible cause for internal id clashes etc...
-				<p> </p>
-				Now with what we have advocated so far, we have a single page controller/model object which is associated
-				with the view. The page controller/model now is stateful, which is exactly what we wanted, however we need
-				component bindings in this bean and those are referenced by the view.
-				The bindings under normal circumstances inherit exactly the scope of the page controller.
-				<p> </p>
+				<p>
+				In order to avoid the lifecycle mismatch between components and beans, any bean that stores a component
+				binding must be request-scoped.
+				</p>
+				<p>
+				However what has been recommended in the "Orchestra best practices" is a flash-scoped backing bean for
+				a view and an additional (flash or manual) conversation-scoped controller for multi-page conversations.
+				The page controller/model now is stateful, which is exactly what we wanted, however when component bindings
+				are needed then they should not be stored on these beans because they are not request-scoped.
+				</p>
+				<p>
 				Therefore the following construct is bound to fail:
 				<pre>
 &lt;component binding="#{viewcontrollerbean.bindingattribute}" /&gt;					
 				</pre>
 				due to the difference in scopes.
-
-                At the first glance, this problem is impossible to solve.
-				With more thoughts, Spring itself provides the solution. Since every bean referenced by
-				the framework and the view layer is a Spring bean, we get the solution out of the "magical
-				Spring toolset".
+				</p>
+				<p>
+                There are two possible solutions to this problem.
+				</p>
 			</subsection>				
-			<subsection name="Solution">
+
+			<subsection name="Solution 1: Spring aop-proxy">
+				<p>
 				Spring in 2.0 has introduced a construct called aop-proxy. This is a special tag which can be embedded
 				into beans which basically does nothing more than to weave a proxy object around an existing object which inherits
 				the scope of the referencing bean.
 				The inner part of the proxy or own bean then can have a scope of its own which can be smaller than the scope
 				of the referencing object.
-				<p>  </p>
-				So how does this help:
-				Lets look again at our example
+				</p>
+				<p>
+				So how does this help? Lets look again at our example
 				<pre>
 &lt;component binding="#{viewcontrollerbean.componentbindingmodel.bindingattribute}" /&gt;					
 				</pre>
+				</p>
+				<p>
 				The accessor path has slightly changed, we have introduced a second bean; a model
 				bean which specifically holds our component bindings.
-				<p> </p>
+				</p>
+				<p>
 				What happens on the spring configuration side is simply the following:
-				
 				<pre>
 &lt;bean id="componentbindingmodel" scope="request" class="path.to.our.model.class"&gt;
    &lt;aop:scoped-proxy /&gt;
@@ -85,17 +111,44 @@
 
 &lt;/bean&gt;
 				</pre>	
-The associated component binding model class is a class which only holds the components as attributes and provides associated setter
-and getter methods.						
-		<p> </p>	
-		What happens at call stage: The viewcontrollerbean is instantiated under conversation scope, the proxy
-		object is generated and assigned depending on its lazy binding stage.
-		At the request end, the content of the componentbindingmodel bean is dropped and only the empty proxy
-		shell is referenced.
-		At the next request - which references parts of the componentbindingmodel object - the content is initialized again
-		and can be accessed. With this, it is possible to bind the request scoped component bindings to any long running scope
-		no matter if they are custom conversation scopes or the session scope!		
+				</p>
+				<p>
+				The associated component binding model class is a class which only holds the components as attributes and
+				provides associated setter and getter methods. The viewcontrollerbean class can then invoke its
+				methods to get access to the bound component instances.						
+				</p>	
+				<p>
+		        When the viewcontrollerbean object is instantiated, Spring sees that it must inject the bean named
+		        "componentbindingmodel" into it. However that bean is marked as an aop:proxy, so what spring injects
+		        is just a "proxy" object, not a real componentbindingmodel bean. Whenever the viewcontrollerbean
+		        then invokes a method on that proxy object, the proxy looks up bean "componentbindingmodel" in 
+		        the specified scope (request), then invokes that method on the object it found. This does have a
+		        significant performance impact; each method call on the proxy performs the bean lookup again. 
+		        However it always acts on the "latest" version of that bean, which would not happen if a real
+		        reference had been held. At the end of the request, the request-scoped componentbindingmodel object
+		        is discarded as normal, and only the proxy "shell" remains. At the next request, a new componentbindingmodel
+		        instance is created and added into the request scope, and methods invoked on the proxy will then
+		        automatically work against this new object.
+				</p>	
 		</subsection>
+			<subsection name="Solution 2: A request-scoped facade">
+			<p>
+			Only a few of the methods on the non-request-scoped backing bean will need access to the bound
+			components. Therefore, these methods can be moved into a request-scoped bean. Add the component
+			binding methods to this request-scoped bean too, and inject a reference to the "real" backing
+			bean into it. Alter the view so that EL expressions that need those component bindings to
+			be evaluated point to the request-scoped bean.   
+			</p>
+			<p>
+			Another way of thinking about this is that the "real" backing bean for the page is implemented as
+			request-scoped, and any state it needs to retain is pushed into a "helper" object that is of
+			conversation scope. The request-scoped bean has access to the components without problems. 
+			</p>
+			<p>
+			While either of these solutions means yet another backing-bean class for the view, there is no
+			"spring magic" required.
+			</p>
+			</subsection>				
 		</section>
 	</body>
 </document>