Author: norman.richards(a)jboss.com
Date: 2008-10-03 20:33:27 -0400 (Fri, 03 Oct 2008)
New Revision: 9187
Modified:
trunk/doc/Seam_Reference_Guide/en-US/Tutorial.xml
Log:
JBSEAM-3026
Modified: trunk/doc/Seam_Reference_Guide/en-US/Tutorial.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Tutorial.xml 2008-10-03 20:05:13 UTC (rev 9186)
+++ trunk/doc/Seam_Reference_Guide/en-US/Tutorial.xml 2008-10-04 00:33:27 UTC (rev 9187)
@@ -526,10 +526,7 @@
<programlisting role="XML"><![CDATA[<?xml
version="1.0" encoding="UTF-8"?>
<components
xmlns="http://jboss.com/products/seam/components"
xmlns:core="http://jboss.com/products/seam/core"
-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation=
- "http://jboss.com/products/seam/core
http://jboss.com/products/seam/core-2.1.xsd
-
http://jboss.com/products/seam/components
http://jboss.com/products/seam/components-2.1.xsd">
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<core:init jndi-pattern="@jndiPattern@"/>
@@ -1187,8 +1184,8 @@
task is complete, the business process ends. </para>
<para> The first JavaBean handles the login screen
<literal>login.jsp</literal>. Its job is just to
- initialize the jBPM actor id using the
<literal>actor</literal> component. (In a real application, it
- would also need to authenticate the user.) </para>
+ initialize the jBPM actor id using the
<literal>actor</literal> component. In a real application, it
+ would also need to authenticate the user.</para>
<example>
<title>Login.java</title>
<programlisting
role="JAVA"><![CDATA[@Name("login")
@@ -1299,7 +1296,7 @@
appear on the same method, because there is usually work to be done using
the application in order to
complete the task. </para>
- <para> Finally, the meat of the application is in
<literal>todo.jsp</literal>: </para>
+ <para> Finally, the core of the application is in
<literal>todo.jsp</literal>: </para>
<example>
<title>todo.jsp</title>
<programlisting role="XHTML"><![CDATA[<%@ taglib
uri="http://java.sun.com/jsf/html" prefix="h" %>
@@ -1445,14 +1442,53 @@
</div>
</h:form>]]></programlisting>
- <para> There are several other files needed for the example, but they
are just standard jBPM and Seam
- configuration and not very interesting. </para>
+
</section>
<section>
<title>How it works</title>
- <para>TODO</para>
+ <para>After logging in, todo.jsp uses the
<literal>taskInstanceList</literal> component to display a table
+ of outstanding todo items for a the current user. Initially there are none.
It
+ also presents a form to enter a new entry. When the user types the todo item
and
+ hits the "Create New Item" button,
<literal>#{todoList.createTodo}</literal> is called. This starts
+ the todo process, as defined in <literal>todo.jpdl.xml</literal>.
</para>
+
+ <para>The process instance is created, starting in the start state and
immediately transition to
+ the <literal>todo</literal> state, where a new task is
created. The task description is set
+ based on the user's
+ input, which was saved to
<literal>#{todoList.description}</literal>. Then, the task is
+ assigned to
+ the current user, which was stored in the seam actor component. Note
that in
+ this example, the process has no extra process state. All the state in
this example
+ is stored in the task definition. The process and task information is
stored in the database
+ at the end of the request.
+ </para>
+
+ <para>
+ When <literal>todo.jsp</literal> is redisplayed,
<literal>taskInstanceList</literal> now finds
+ the task that was just created.
+ The task is shown in an <literal>h:dataTable</literal>. The
internal state of the task is
+ displayed in
+ each column: <literal>#{task.description}</literal>,
<literal>#{task.priority}</literal>,
+ <literal>#{task.dueDate}</literal>, etc... These fields
+ can all be edited and saved back to the database.
+ </para>
+
+ <para>Each todo item also has "Done" button, which calls
<literal>#{todoList.done}</literal>. The
+ <literal>todoList</literal> component
+ knows which task the button is for because each s:button specificies
+ <literal>taskInstance="#{task}"</literal>,
referring
+ to the task for that particular line of of the table. The
<literal>@StartTast</literal> and
+ <literal>@EndTask</literal> annotations
+ cause seam to make the task active and immediately complete the task. The
original process then
+ transitions into the <literal>done</literal> state, according to
the process definition, where it ends.
+ The state of the task and process are both updated in the database.
+ </para>
+
+ <para>When <literal>todo.jsp</literal> is displayed again,
the now-completed task is no longer
+ displayed in the
+ <literal>taskInstanceList</literal>, since that component only
display active tasks for the user.</para>
</section>
</section>
@@ -1849,7 +1885,55 @@
<section>
<title>How it works</title>
- <para>TODO</para>
+ <para>We'll step through basic flow of the application. The game
starts with the
+ <literal>numberGuess.jspx</literal> view. When the page is
first displayed, the
+ <literal>pages.xml</literal> configuration causes
conversation to begin and associates
+ the <literal>numberGuess</literal> pageflow
+ with that conversation. The pageflow starts with a
<literal>start-page</literal> tag,
+ which is a wait state, so the
<literal>numberGuess.xhtml</literal> is rendered.
+ </para>
+
+ <para>The view references the
<literal>numberGuess</literal> component, causing a new
+ instance to be created and stored in the conversation. The
<literal>@Create</literal> method
+ is called, initializing the state of the game. The view displays an
<literal>h:form</literal>
+ that allows the user to edit
<literal>#{numberGuess.currentGuess}</literal>.
+ </para>
+
+ <para>The "Guess" button triggers the
<literal>guess</literal> action. Seam defers to the pageflow
+ to handle the action, which says that the pageflow should transition to the
<literal>evaluateGuess</literal>
+ state, first invoking <literal>#{numberGuess.guess}</literal>,
which updates the guess count
+ and highest/lowest suggestions in the
<literal>numberGuess</literal> component.
+ </para>
+
+ <para> The <literal>evaluateGuess</literal> state checks
the value of <literal>#{numberGuess.correctGuess}</literal>
+ and transitions either to the <literal>win</literal> or
<literal>evaluatingRemainingGuesses</literal>
+ state. We'll assume the number was incorrect, in which case the pageflow
transitions to
+ <literal>evaluatingRemainingGuesses</literal>. That is also a
decision state, which
+ tests the <literal>#{numberGuess.lastGuess}</literal> state
to determine whether or not the user has
+ more guesses. If there are more guesses
(<literal>lastGuess</literal> is <literal>false</literal>),
+ we transition back to the original
<literal>displayGuess</literal> state. Finally we've
+ reached a page state, so the associated page
<literal>/numberGuess.jspx</literal> is displayed.
+ Since the page has a redirect element, Seam sends a redirect to the the
user's browser,
+ starting the process over.
+ </para>
+
+ <para>
+ We won't follow the state any more except to note that if on a future
request either the
+ <literal>win</literal> or the
<literal>lose</literal> transition were taken, the user would
+ be taken to either the <literal>/win.jspx</literal> or
<literal>/lose.jspx</literal>.
+ Both states specify that Seam should end the conversation, tossing away
all the game state and
+ pageflow state, before redirecting the user to the
+ final page.
+
+ </para>
+
+ <para>The numberguess example also contains Giveup and Cheat buttons.
You should be able to
+ trace the pageflow state for both actions relatively easily. Pay
particular attention
+ to the <literal>cheat</literal> transtition, which loads a
sub-process to handle that flow.
+ Although it's
+ overkill for this application, it does demonstrate how complex pageflows
can be broken down into
+ smaller parts to make them easier to understand.
+ </para>
</section>
</section>
@@ -1930,21 +2014,27 @@
</ulink>
</para>
- <para> Just nine classes (plus six session beans local interfaces)
where used to implement this application.
- Six session bean action listeners contain all the business logic for the
listed features. </para>
+ <para>The application uses six session beans for to implement the
business logic for the listed features. </para>
<itemizedlist>
<listitem>
+ <para><literal>AuthenticatorAction</literal>
provides the login authentication logic.</para>
+ </listitem>
+ <listitem>
<para><literal>BookingListAction</literal>
retrieves existing bookings for the currently logged in user. </para>
</listitem>
<listitem>
<para><literal>ChangePasswordAction</literal>
updates the password of the currently logged in user.</para>
</listitem>
<listitem>
- <para><literal>HotelBookingAction</literal>
implements the core functionality of the application: hotel
- room searching, selection, booking and booking confirmation. This
functionality is implemented as a
- <emphasis>conversation</emphasis>, so this is the
most interesting class in the application. </para></listitem>
+ <para><literal>HotelBookingAction</literal>
implements booking and confirmation
+ functionality. This functionality is implemented as a
+ <emphasis>conversation</emphasis>, so this is one of
the most interesting classes in the
+ application.</para></listitem>
<listitem>
+ <para><literal>HotelSearchingAction</literal>
implements the hotel search functionality.
+ </para></listitem>
+ <listitem>
<para><literal>RegisterAction</literal> registers a
new system user.</para>
</listitem>
</itemizedlist>
@@ -1972,9 +2062,10 @@
conversation. The user can select multiple hotels from the same search
results page, in different
browser tabs. </para>
<para> Most web application architectures have no first class construct
to represent a conversation. This
- causes enormous problems managing state associated with the conversation.
Usually, Java web applications
- use a combination of two techniques: first, some state is thrown into
the
- <literal>HttpSession</literal>; second, persistable state is
flushed to the database after every
+ causes enormous problems managing conversational state. Usually, Java web
applications
+ use a combination of several techniques. Some state can be transfered in
the URL.
+ What can't is either thrown into the
+ <literal>HttpSession</literal> or flushed to the database
after every
request, and reconstructed from the database at the beginning of each new
request. </para>
<para> Since the database is the least scalable tier, this often
results in an utterly unacceptable lack of
scalability. Added latency is also a problem, due to the extra traffic to
and from the database on every
@@ -1984,36 +2075,42 @@
with the data. Furthermore, because the cache is shared between many
concurrent transactions, we've
introduced a whole raft of problem's associated with keeping the
cached state consistent with the
database. </para>
- <para> Now consider the state held in the
<literal>HttpSession</literal>. By very careful programming, we
- might be able to control the size of the session data. This is a lot more
difficult than it sounds,
- since web browsers permit ad hoc non-linear navigation. But suppose we
suddenly discover a system
- requirement that says that a user is allowed to have
<emphasis>mutiple concurrent
- conversations</emphasis>, halfway through the development of the
system (this has happened to me).
- Developing mechanisms to isolate session state associated with different
concurrent conversations, and
- incorporating failsafes to ensure that conversation state is destroyed
when the user aborts one of the
- conversations by closing a browser window or tab is not for the faint
hearted (I've implemented this
- stuff twice so far, once for a client application, once for Seam, but
I'm famously psychotic). </para>
- <para> Now there is a better way. </para>
+ <para> Now consider the state held in the
<literal>HttpSession</literal>. The HttpSession is great
+ place for true session data, data that is common to all requests that the
user has with the application.
+ However, it's a bad place to store data related to individual series
of requests. Using the session of
+ conversational quickly breaks down when dealing with the back button and
multiple windows.
+ On top of that, without careful
+ programming, data in the HTTP Session can grow quite large, making the
HTTP session difficult
+ to cluster. Developing mechanisms to isolate session state associated
with different concurrent
+ conversations, and incorporating failsafes to ensure that conversation
state is destroyed when
+ the user aborts one of the
+ conversations by closing a browser window or tab is not for the faint
hearted. Fortunately, with Seam,
+ you don't have to worry about that.
+ </para>
+
<para> Seam introduces the <emphasis>conversation
context</emphasis> as a first class construct. You can
safely keep conversational state in this context, and be assured that it
will have a well-defined
lifecycle. Even better, you won't need to be continually pushing data
back and forth between the
application server and the database, since the conversation context is a
natural cache of data that the
user is currently working with. </para>
- <para> Usually, the components we keep in the conversation context are
stateful session beans. (We can also
- keep entity beans and JavaBeans in the conversation context.) There is an
ancient canard in the Java
- community that stateful session beans are a scalability killer. This may
have been true in 1998 when
- WebFoobar 1.0 was released. It is no longer true today. Application
servers like JBoss AS have extremely
- sophisticated mechanisms for stateful session bean state replication.
(For example, the JBoss EJB3
- container performs fine-grained replication, replicating only those bean
attribute values which actually
- changed.) Note that all the traditional technical arguments for why
stateful beans are inefficient apply
+ <para> In this application, we'll use the conversation context to
store stateful session beans.
+ There is an ancient canard in the Java
+ community that stateful session beans are a scalability killer. This may
have been true in the
+ early days of enterprise Java, but it is no longer true today. Modern
application servers have
+ extremely
+ sophisticated mechanisms for stateful session bean state replication.
JBoss AS, for example, performs
+ fine-grained replication, replicating only those bean attribute values
which actually
+ changed. Note that all the traditional technical arguments for why
stateful beans are inefficient apply
equally to the <literal>HttpSession</literal>, so the
practice of shifting state from business tier
stateful session bean components to the web session to try and improve
performance is unbelievably
misguided. It is certainly possible to write unscalable applications
using stateful session beans, by
using stateful beans incorrectly, or by using them for the wrong thing.
But that doesn't mean you should
- <emphasis>never</emphasis> use them. Anyway, Seam guides
you toward a safe usage model. Welcome to
- 2005. </para>
- <para> OK, I'll stop ranting now, and get back to the tutorial.
</para>
+ <emphasis>never</emphasis> use them.
+ If you remain unconvinced, Seam allows the use of POJOs instead of
stateful session beans.
+ With Seam, the choice is yours.
+ </para>
+
<para> The booking example application shows how stateful components
with different scopes can collaborate
together to achieve complex behaviors. The main page of the booking
application allows the user to
search for hotels. The search results are kept in the Seam session scope.
When the user navigates to one
@@ -2024,10 +2121,10 @@
the use of handwritten JavaScript. </para>
<para> The search functionality is implemented using a session-scope
stateful session bean, similar to the
- one we saw in the message list example above. </para>
+ one we saw in the message list example. </para>
<example>
- <title></title>
+ <title>HotelSearchingAction.java</title>
<!-- Can't use code hightlighting with callouts -->
<programlistingco>
<areaspec>
@@ -2144,7 +2241,7 @@
<para> The main page of the application is a Facelets page. Let's
look at the fragment which relates to
searching for hotels: </para>
<example>
- <title></title>
+ <title>main.xhtml</title>
<!-- Can't use code hightlighting with callouts -->
<programlistingco>
<areaspec>
@@ -2230,8 +2327,8 @@
perform a partial page update when the asynchronous
response is received. </para>
</callout>
<callout arearefs="booking-status-element">
- <para> The RichFaces Ajax
<literal><a:status></literal> tag lets us display a cheesy
- annimated image while we wait for asynchronous requests
to return. </para>
+ <para> The RichFaces Ajax
<literal><a:status></literal> tag lets us display an
+ animated image while we wait for asynchronous requests to
return. </para>
</callout>
<callout arearefs="booking-outputpanel-element">
<para> The RichFaces Ajax
<literal><a:outputPanel></literal> tag defines a region of
@@ -2264,7 +2361,7 @@
pretty long. But if you think of it as a list of scripted actions that
implement the various steps of
the conversation, it's understandable. Read the class from top to
bottom, as if it were a story. </para>
<example>
- <title></title>
+ <title>HotelBookingAction.java</title>
<!-- Can't use code hightlighting with callouts -->
<programlistingco>
<areaspec>
@@ -2416,28 +2513,6 @@
</section>
<section>
- <title>The Seam UI control library</title>
- <para> If you check inside the WAR file for the booking application,
you'll find
- <literal>seam-ui.jar</literal> in the
<literal>WEB-INF/lib</literal> directory. This package contains a
- number of JSF custom controls that integrate with Seam. The booking
application uses the
- <literal><s:link></literal> control for
navigation from the search screen to the hotel
- page: </para>
-
- <programlisting role="JAVA"><![CDATA[<s:link
value="View Hotel"
action="#{hotelBooking.selectHotel(hot)}"/>]]></programlisting>
-
- <para> The use of <literal><s:link></literal>
here allows us to attach an action listener to a
- HTML link without breaking the browser's "open in new
window" feature. The standard JSF
- <literal><h:commandLink></literal> does not
work with "open in new window". We'll see
- later that <literal><s:link></literal> also
offers a number of other useful features,
- including conversation propagation rules. </para>
-
- <para> The booking application uses some other Seam and RichFaces Ajax
controls, especially on the
- <literal>/book.xhtml</literal> page. We won't get
into the details of those controls here, but if
- you want to understand this code, please refer to the chapter covering
Seam's functionality for JSF form
- validation. </para>
- </section>
-
- <section>
<title>The Seam Debug Page</title>
<para> The WAR also includes
<literal>seam-debug.jar</literal>. The Seam debug page will be availabled