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:17:10 UTC
svn commit: r590710 - in /myfaces/orchestra/trunk/core/src/site/xdoc:
bestPractice.xml conversation.xml
Author: skitching
Date: Wed Oct 31 08:17:10 2007
New Revision: 590710
URL: http://svn.apache.org/viewvc?rev=590710&view=rev
Log:
Update best-practices recommendation. Other minor doc tweaks.
Modified:
myfaces/orchestra/trunk/core/src/site/xdoc/bestPractice.xml
myfaces/orchestra/trunk/core/src/site/xdoc/conversation.xml
Modified: myfaces/orchestra/trunk/core/src/site/xdoc/bestPractice.xml
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/site/xdoc/bestPractice.xml?rev=590710&r1=590709&r2=590710&view=diff
==============================================================================
--- myfaces/orchestra/trunk/core/src/site/xdoc/bestPractice.xml (original)
+++ myfaces/orchestra/trunk/core/src/site/xdoc/bestPractice.xml Wed Oct 31 08:17:10 2007
@@ -33,6 +33,13 @@
<subsection name="Single Page Conversation With Flash Scope">
+ <p>
+ This applies when you have a single page that the user may submit multiple times
+ (eg select an item from a list and click a button, then select a different item
+ and click another button). The state of the page changes after each submit, but
+ this state must be held in memory.
+ </p>
+
<p>
Write a single managed bean for the view. Configure this bean as belonging
to an unnamed "flash" conversation scope and reference it as normal from
@@ -41,10 +48,10 @@
<p>
The conversation will be started when the bean is first accessed, and will
- automatically be terminated when navigation to some other page occurs
- (well, technically when a page is rendered that does not reference this
- managed bean). If you have an action method that wants to "start fresh"
- while remaining at the same view, then from the action method call
+ automatically be terminated (ie the bean will be deleted) when navigation
+ to some other page occurs (well, technically when a page is rendered that
+ does not reference this managed bean). If you have an action method that wants
+ to "start fresh" while remaining at the same view, then from the action method call
ConversationUtils.invalidateAndRestartCurrent() to discard the current
conversation.
</p>
@@ -52,21 +59,30 @@
<p>
If this bean is named to match the view that it is backing
(eg beanname="edit" for the "/edit" viewid), then it is trivial
- to get "lifecycle events" from Orchestra's ViewController. See the
+ to get "lifecycle events" from Orchestra's ViewController. Even if
+ the bean-name does not match the view, there are ways of configuring
+ things so it still receives the appropriate callbacks. See the
ViewController documentation for more information on this.
</p>
</subsection>
<subsection name="Multi Page Conversation With Flash Scope">
-
+ <p>
+ This applies when you have a set of pages that cooperate together to interact
+ with the user, but where the state used by these pages must be held in memory.
+ Commonly, sequences of pages like this eventually lead to a "save" or "execute"
+ button which then performs an action using the state that was set up by the user
+ via the preceding pages. Such sequences are sometimes called a "wizard".
+ </p>
+
<p>
Use one flash-scoped controller for the whole conversation, plus one simple
- request-scoped bean per view. For example, if you have three pages then structure
- things as follows:
+ request-scoped or flash-scoped bean per view. For example, if you have three
+ pages then structure things as follows:
<ul>
- <li>Page:purchaseStep1.jsp</li>
+ <li>Page:purchaseStep1.jsp<br/>Bean name: purchaseStep1</li>
<li>Page:purchaseStep2.jsp<br/>Bean name: purchaseStep2</li>
<li>Page:purchaseStep3.jsp<br/>Bean name: purchaseStep3</li>
<li>Conversation bean name: purchaseController</li>
@@ -74,41 +90,72 @@
</p>
<p>
- The request-scoped beans are used only to check whether the conversation is really running; this avoids
- problems when users leap into the middle page of a sequence (eg via a bookmark). EL expressions in
- all of the pages reference the purchaseController object, not the request-scoped beans.
- </p>
-
- <p>
- We have to ensure that the user starts with the start page. Reasons why this might be violated are:
- <ul>
- <li>the conversation timed out in the mid of the work</li>
- <li>the user tampered with the url</li>
- </ul>
- To ensure a running conversation, simply add a method called <code>initView()</code>
- to the purchaseStep2 and purchaseStep3 beans which looks like the following:
- <pre>
-public void initView()
-{
- ConversationUtils.ensureConversationRedirect("pagesController", "/startPage.faces");
-}
+ Generally, there is no need to specify a conversationName attribute for these beans,
+ ie the name of the conversation they are in is the same as the name of the bean.
+ </p>
+ <p>
+ The per-view beans handle logic and state that is specific to that page. If there is state needed
+ for the page, then use "flash" scope, otherwise use "request" scope. Inject the controller bean
+ into each per-view bean so that state and logic which is shared between views can be accessed.
+ </p>
+ <p>
+ If there is common logic that each page shares, then that can be defined in an abstract base bean
+ which the per-view beans extend.
+ </p>
+ <p>
+ EL expressions in the pages can reference either the per-view bean or the common controller bean,
+ whichever is appropriate.
+ </p>
+ <p>
+ There are two problems with workflows:
+ <ul>
+ <li>
+ <p>
+ A conversation may timeout in the middle of a conversation
+ (eg the user goes to lunch, then tries to continue on return), and
+ </p>
+ </li>
+ <li>
+ <p>
+ A user may try to leap into the middle of a conversation (eg via a bookmark)
+ </p>
+ </li>
+ </ul>
+ With the above recommended structure, each per-view bean except the first one can then
+ check whether the conversation exists and is in an appropriate state for that view.
+ If not, then a navigation to the proper "entry page" for the conversation can be done.
+ </p>
+ <p>
+ The basic check for the existence of the conversation is fairly simple to do in java code:
+ <pre>
+public void initView()
+{
+ ConversationUtils.ensureConversationRedirect("purchaseController", "/purchaseStep1.jsf");
+}
</pre>
+ If a set of per-view beans share a common base class then this initView method can be
+ added to the base class, then overridden only in the page that is redirected to
+ ("purchaseStep1.jsp" in this example) to prevent circular redirects. This then protects
+ all of the pages from access without a correctly initialised conversation.
+ </p>
+
+ <p>
+ Without this check, when a user leaps into the middle of a conversation, EL expressions
+ will trigger the creation of the missing purchaseController (and its associated conversation)
+ but the bean probably does not have the appropriate state to render the page correctly.
+ </p>
- As noted above, Orchestra's ViewController will invoke "lifecycle events" for a view
- onto any bean that has a "matching name". Because the request-scoped beans above have
- names that match the viewid, the initView method on the appropriate bean is called
- when a request for that view is received, and a check can be made for the existence
- of the right conversation. Without this check, when a user leaps into the middle
- of a conversation, EL expressions will trigger the creation of the missing
- purchaseController (and its associated conversation) but the bean probably does
- not have the appropriate state to render the page correctly.
+ <p>
+ The orchestra core15 module provides the @ConversationRequire annotation to make this even easier.
</p>
<p>
- There is no need for a bean to back the first page (purchaseStep1) because it is
- perfectly valid for the user to enter that page with no existing conversation.
+ Note that this works even when the purchaseController bean (which is in the purchaseController
+ conversation, unless otherwise configured) is injected into the per-view bean. The object
+ injected is actually a proxy, so the conversation is not created until the bean is really
+ referenced.
</p>
-
+
<p>
<b>Notice:</b> There is also a <code>JsfConversationUtils</code>-class which allows
you to invoke a navigation rule rather than encoding a url in the call to
@@ -116,9 +163,11 @@
</p>
<p>
- An alternative to declaring "dummy beans" just to receive lifecycle callbacks
- is to customise the way the ViewController maps viewids to beannames. In
- particular, see the ViewController annotations from the Orchestra core15 module.
+ If the views in a workflow are so simple that there is no logic or state needed, then
+ rather than declaring "dummy beans" just to receive lifecycle callbacks the way the
+ ViewController maps viewids to beannames can be configured. In particular, see the
+ ViewController annotations from the Orchestra core15 module. Having a separate bean
+ for the "entry page" of a workflow is always a good idea however.
</p>
</subsection>
@@ -138,6 +187,27 @@
</subsection>
+ <subsection name="Avoid Session Scope">
+ <p>
+ In almost all cases, using Session scope is a bad idea. All sorts of data goes into
+ sessions, including data from UI frameworks, and sometimes from the servlet engine
+ itself. Instead of using session-scope, put all such beans into a single conversation
+ scope called "session". The most significant benefit from this is that Orchestra's
+ "conversation context" feature now allows a user to open multiple windows to your
+ app without problems; each window has a different "conversation context", and each
+ "conversation context" has a completely independent set of conversations - including
+ all the beans in the "session" conversation. It's almost like the user connecting
+ from two different machines - except that any application login (authentication)
+ data is shared.
+ </p>
+ <p>
+ There are a few places where real session-scoped data might be appropriate, but not
+ many. Think whether two separate browser windows for a user really should share that
+ data. And if they should, then make sure that the shared objects are thread-safe, as
+ two concurrent requests from two different windows will be using the same instance.
+ </p>
+ </subsection>
+
<subsection name="Component bindings">
<p>
We recommend you read about <a href="component-bindings.html">component binding and the scoping problem</a>.
@@ -148,4 +218,4 @@
</section>
</body>
-</document>
\ No newline at end of file
+</document>
Modified: myfaces/orchestra/trunk/core/src/site/xdoc/conversation.xml
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/site/xdoc/conversation.xml?rev=590710&r1=590709&r2=590710&view=diff
==============================================================================
--- myfaces/orchestra/trunk/core/src/site/xdoc/conversation.xml (original)
+++ myfaces/orchestra/trunk/core/src/site/xdoc/conversation.xml Wed Oct 31 08:17:10 2007
@@ -30,8 +30,8 @@
<p>
Often a logical operation consists of multiple JSF requests to the server.
For example, purchasing an insurance policy requires completing a number
- of related forms (often referred to as a "conversation" or a "dialog"),
- during which the same java objects need to be kept in memory.
+ of related forms (often referred to as a "conversation", "workflow" or
+ "dialog"), during which the same java objects need to be kept in memory.
</p>
<p>