SEAM IN ACTION 第7章


Part 3 Seam’s state management

Part 1 presented the motivation for why Seam was created and demonstrated
ways it simplifies development of web applications. You used seam-gen to quickly
put together a Seam-based application and agile development environment.
Part 2 got into the guts of Seam by teaching you to define and configure components
and getting them to communicate. What sets Seam apart from other weboriented
frameworks is its focus on state management. This term may not mean
much to you right now, but trust that it plays a key role in what you’ll learn to
master in the next three chapters: conversations, page flows, the extended persistence
context, application transactions, and entity home components.

SEAM状态管理
第一部分显示了SEAM的创作动机,看到了为对WEB应用开发的简化。使用seam-gen快速建立基于SEAM的应用及快速开发环境。
第二部件教你定义和配置部件并使它们互相通讯。让SEAM与其它面向WEB的框架不同的是它的状态管理。这个词汇现在对你来说还没什么感觉。但会在下三章作为关键角色。conversations, page flows, the extended persistencecontext, application transactions, and entity home components.

Chapter 7 introduces conversations as a way to effectively string together
requests. You define conversation boundaries using a familiar declarative
approach. You also learn to orchestrate a conversation with a page flow and to
let the user multitask using workspaces.

第7章介绍会话作为高效的带请求的串。你用近似的方式来定义会话边界,你也可以用页面流来安排会话,让用户使用工作空间以运行多任务。

Chapter 8 puts conversations aside initially to cover Java persistence, the ORM
mechanism that translates Java objects to and from database records. The end of
the chapter sees a return to state management when the extended persistence
context in EJB 3 is introduced. This construct ensures persistent objects remain
managed so that updates to the database require no programming, related
objects can be fetched on demand, and database reads are kept to a minimum.

第8章讲Java持久化。ORM机制,章节末尾看看当EJB3引入扩展的存储上下文时状态管理的返回。这个结构确保持久对象保持被管理,以使更新数据库请求非编程化,相关的对象可以应招拿到,对数据库的访问次数保持在最小。

In chapter 9, you learn that conversations provide the perfect vehicle for
extending the persistence context. This chapter introduces Seam-managed persistence
as an alternative to its complement in Java EE, with a number of extensions
weaved in. More important, propagation of the persistence context is
handled transparently, a relief from complex rules in EJB 3. Seam also offers a
unique approach to transactions by wrapping them around each request and
facilitating application transactions that span multiple requests.

第9章,会话提供扩展存储上下文的途径。介绍作为Java EE实现的SEAM管理的持久化。更重要的是持久上下文传播可透明地处理,是复杂的EJB 3的规则的简化。SEAM也提供一种独特的方法来将每个请求包装,让应用事务跨越多个请求。

In chapter 10, you get to bring together everything you have learned about Seam
and perform rapid development using the Seam Application Framework. This chapter
helps you appreciate how much you have matured as a Seam developer throughout
this part.

第10章,要将全部所学综合起来做个快速开发。本章让你体验作为一个SEAM开发者的成熟感。

7、The conversation: Seam’s unit of work


This chapter covers
■ Managing state with a conversation
■ Controlling long-running conversations
■ Switching between workspaces
■ Defining stateful page flows

会话:SEAM的工作部
管理会话状态
控制长期运行的会话
切换工作区间
定义有状态页流

Seam helps establish a rich user experience by stretching the boundaries of a unit
of work to cover a use case—a determinate interchange between the user and the
application. In this chapter, you’ll learn how Seam’s conversation context can host
the working set of data needed to support this interchange. Seam’s conversation is
contrasted with traditional state management techniques, demonstrating how it
both relieves the burden of handling this task and gives multipage interactions a
formal representation in a web application. Conversations open the door to more
advanced state management techniques—such as stateful page flows, nested conversations,
and workspaces—that further enrich the user experience.

Conversations are one of Seam’s crowning features, touching many areas of the
framework and bridging earlier chapters with those that lie ahead. In fact, CRUD
applications created by seam-gen use conversations to manage the use case of adding
and modifying an entity instance. To witness conversations in action before beginning
this chapter, you can study them at work in the Open 18 application. This chapter
starts off by defining a use case and then explores how aligning it with a stateful context
can improve the user’s experience.

SEAM通过扩展工作单元的边界到整个用例来增强用户体验。本章你会学到SEAM会话上下文是怎样管理来和交换的这些数据。
SEAM的会话的对等物是传统的状态管理技术,包含了更先进的状态管理技术,如有状态页流,嵌套会话,工作空间,这些都增强了用户体验。
会话是SEAM的王牌特点。事实上,seam-gen建立起来的CRUD应用,去增加或维护实体实例,都要用到。在学习本章前,可以先看一下Open 18应用。
本章的流程就是定义一个用例,然后显如何能过有状态上下文来增强用户体验。

7.1 Learning to appreciate conversational state
After returning from your golf getaway, you learn that, in your absence, someone has
been having fun at your expense, courtesy of your credit card number. To reclaim
your assets, you have to endure the disparate customer service process deployed by
your bank. I’m sure this exchange will strike a familiar chord.
“How can I help you?”

因为你的高尔夫逃亡(getaway),你知道有人用了你的信用卡。为了收回你的资产,你不得不分别经历各种服务过程。


You take this cue to launch into your rant about being a victim of fraud, your
whereabouts during the previous week, and which charges you’re disputing.
A brief pause is interrupted by, “Can I have your account number?”
Your momentum is temporarily interrupted as you wait for your call to be logged.
The agent then informs you that you need to be transferred to the fraud department,
which is properly equipped to deal with this matter. There’s no chance to object as the
switch happens without delay. A new voice appears on the other end of the line.
“How can I help you?”
Sigh. Time to start over from the beginning.
You began the day a victim of fraud. Now you have become a victim of a stateless
process. Critical information about your situation failed to make the leap from the customer
service representative to the fraud representative. This mishap could have been
avoided had the two representatives
engaged in a conversation during
the switch to retain a record of your
story. Unfortunately for you, they
don’t see the big picture. As soon as
you’re handed off, there’s another
call to answer.
That’s exactly how requests
are handled in a web application,
which rests atop a stateless protocol
(HTTP). The result is that data
tends to be dropped between page
requests, blind to the ongoing use
case. Seam acknowledges that all
requests occur in the scope of a conversation,
that a conversation can
span multiple requests, and that
conversations are subsets of a user’s
session, as illustrated in figure 7.1.

Through use of conversations, Seam supports building stateful interactions that
bridge the gap between requests. As a result, state can be tracked until the user’s goal
is accomplished, not just to satisfy an atomic step along the way.

通过使用会话,SEAM支持建立有状态交互,添补了多个请求间的断层。


7.1.1 Redefining the unit of work
You may think of a unit of work in terms of a database transaction. The story just told
is faithful to this definition at the expense of the caller’s time. This short-lived unit of
work is problematic because it’s not stateful (in the long-running sense). Seam redefines
the unit of work by looking at it from the user’s perspective, coining what is
known as a conversation. During a conversation, database transactions, page requests,
and other atomic units of work may come and go, but the overall process isn’t deemed
complete until the user’s goal is accomplished. In a conversation, the state is said to be
extended. In the next chapter, you’ll learn that the persistence context can also be
extended to match this timetable.
The linkage between page requests in a conversation is established through the
use of a special token and a partitioning of the HTTP session, which you’ll learn about
in section 7.2. The conversation’s lifetime is controlled by declarative boundary conditions,
covered in section 7.3. First, I want to focus on the challenge of establishing a
well-defined stateful context in a web application and how this task has been traditionally
handled. You could go so far as to say that propagating state in a web application is
a downright burden. After studying some of the alternatives, I’ll segue into how
Seam’s conversation provides relief.

重定义工作单元
你可能认为工作单元就是数据库的事务。这正是所谓的短命的工作单元。SEAM从用户的视角出发重新定义了工作单元。在会话过程中,数据库事务,页请求一次次出现,但整个过程只有当用户的目标完成才算结束。
首先我讲一下怎样建立一个定义良好的有状态上下文。


7.1.2 The burden of managing state
All applications have state, even those classified as stateless (e.g., RESTful applications).
Some applications stash state away in server-side contexts such as the HTTP session
scope or the JSF page scope. So-called stateless applications just weave state into
the URL or hide it away in hidden form fields. A majority of applications likely use a
mix of these strategies. The real question is, as users traverse from one page to the
next, how much support do they get from the framework for managing and accessing
that state?
I’m sure that in the past you have worked very hard to save data between requests and
subsequently prepare it for use in the business logic. Whether you wrote one of those
applications that has more hidden form fields than visible ones or one with as many
Struts ActionForm classes as business components, you have felt the burden of managing
state. That’s not to say that RESTful URLs, hidden form fields, request parameters,
or cookies aren’t viable. It’s just that when they are means to an end, where the end is
to restore the state from the previous request, they cause a lot of work. Seam attempts
to make state readily available in contexts that closely represent the life cycle of that state
(i.e., a use case). Not only does the conversation context blend state between requests
for the duration of a use case, it avoids destroying the identity of objects as a result of
serialization, which is the main problem with the traditional approaches.

所有的应用都是有状态的,既便是那些所谓的无状态(如RESTful应用)。一些应用将状态放在服务器端的HTTP
Session scope or the JSF page scope。所谓的无状态应用只是将状态织入了URL或隐含在隐藏域中。
但问题是当用户做页面切换是地,状态受到了多少支持?

我确信以前你要做很多努力来传数据。

PASSING DATA THROUGH THE REQUEST
One approach to propagating state is to send it along as part of the request in one of
these forms:
■ Request parameters (i.e., hidden form fields or the query string)
■ As part of the URL (e.g., /course/view/9)
■ Browser cookies
All of these options work by disassembling some server-side object into bite-sized
chucks, tunneling those parts through the request as string values, then reassembling
the objects on the server, as figure 7.2 illustrates. There are times when a RESTful URL,
request parameter, or cookie is the right tool for the job. However, if you’re working
with any decent-size set of data, having to prepare this translation on every request is
downright tedious.


通过请求传参:
请求参数
重写URL
浏览器cookies

这些方式都是将数据切成小块。每个请求都变得很罗嗦。


You learned in chapter 3 that Seam’s page parameters offer some relief here by automatically
translating objects to and from HTTP request parameters on a per-page basis.
The downside to page parameters is that they are hardwired to the page definition. Since
a page may be used in more than one set of circumstances, the configuration could result
in data being propagated even when you don’t need it; or worse, it gets in the way.

The biggest drawback of parameter-based propagation in general is that the serverside
object loses its identity when it passes through this funnel. The object that’s built
on the server, beyond the request boundary, is a clone of the original object, possibly
even a partial one. This cloning process makes it impossible to transfer a resource that
relies on its identity being maintained, such as a persistence manager or managed
entity instance. For an object’s identity to be preserved, the object must be stored in a
server-side context, such as the JSF UI component tree (which requires server-side state
saving), the HTTP session, or as you’ll soon learn, the conversation. Then it’s only necessary
to pass a token to the server to restore the context and the objects it contains.


第三章已学过,SEAM页参数会基于每页而自动做HTTP请求参数的转换。页参数不好的地方是硬连接到页定义。因为页可能会用到多个环境,这样的配置会在你不需要的时候也进行数据传递。

基于参数传递的最大的缺点是服务器端对象丢失了它的标识。服务器上建立的对象,超出的请求的边界,是原对象的克隆,或只是一部分。这个克隆不可能带着全部信息,如存储管理器或被管理的实例对象,对于一个被保存的对象的标识,对象必须保存在服务器的上下文件。如JSF UI部件树、HTTP session、或conversation。只需要传来一个标识,一来保存上下文及包含的对象。


STASHING STATE IN THE JSF UI COMPONENT TREE
You learned in chapter 4 that the root of the JSF UI component tree has an attribute
map that can be used to save data across a JSF postback. Seam’s page context
and the component tag from the MyFaces Tomahawk library both
offer transparent access to this map, among alternatives. As elegant as these abstraction
layers may be, it doesn’t diminish the fact that this map is merely the JSF equivalent
of hidden form fields.
Using the UI component tree as a stateful context has problems that match those
cited previously. First, you must reestablish the set of variables in the page context when
the UI component tree is rebuilt (which happens during any postback request that
issues a redirect or renders a different view). The second problem is that the UI component
tree doesn’t guarantee that the identity of the objects will be maintained. JSF
state saving can be done either on the client side or the server side. When client-side
state saving is used, the restored objects are clones of the originals, having the same
problem as passing parameters through the request. If you don’t have control over the
state-saving setting, it’s best not to rest an object’s identity on such unstable ground.
The challenge of maintaining object identity is often solved by using the HTTP
session.

隐藏状态于JSF UI部件树

第四章已学过,JSF UI根部件树有一个属性map,可用来在一个JSF postback中保存数据。SEAM来自于MyFaces Tomahawk库的上下文和部件标签,都提供对这个map的透明访问。这些map只是作为隐含域来用。
使用UI部件树来保存上下文有些问题。
首先当UI部件树重建时,你必须在页上下文重建一套变量。(任何发出重定向或重画请求的postback请求都会发生)。
第二是UI部件树不保证保管对象的标识。JSF的状态是可以保存在服务器端或客户端的。当用客户端保存时,被保存的对象是原对象的克隆, 这与通过请求传参的问题是一样的。如果对你state-saving setting没有控制权,所以最好不要用这类对象标识不可靠的情况。
经常用HTTP session来解决这一问题


STORING DATA IN THE HTTP SESSION
The HTTP session can be used to store arbitrarily complex objects while preserving
their identity. This context is shared by all browser tabs and windows that restore the
same session token and typically lasts on the order of hours or days. While this storage
mechanism sounds ideal, it’s unfortunately too good to be true. The main downfall of
the HTTP session is that it quickly becomes a tangled mess of data that consumes
excessive amounts of memory and complicates multiwindow application usage. Let’s
explore these issues.
The HTTP session is well suited for data that you want to retain across all requests
made by a given user, such as the user’s identity. However, the session isn’t a good
place to store data for a specific use case. It may seem harmless when the user is
accessing the application from a single window—but what happens when the user
spawns multiple tabs or windows? Because the session identifier is stored as a cookie in
the user’s browser, and most browsers share cookies between tabs and windows
(herein referred to as tabs), the result is that the multiple tabs naively share the same
session data. If the application doesn’t recognize this sharing, it can cause data to be
manipulated in conflicting ways.


在保留标识符的情况下,HTTP session可以操作任意复杂的对象。这个上下文被所有保存同一进程标志的浏览器标签或窗口共享,通常可以持续数小时或数天。虽然这一机制听上去完美,但是很容易产生混乱,因为占用了过多的内存。

当你希望数据,如用户身分标识,在所有请求中保留时,HTTP session机制当然好。但session不适合保存用例。当从单个窗口来访问时,没什么问题,多窗口或多标签就完了。因为session标识符是使用cookie保存在用户的浏览器中的,大多数浏览器在多个标签或窗口间共享。所以多个标签会天真地共享同一session数据,如果应用不认识这个共享,则数据会出现操作冲突。


NOTE
The session identifier can also be passed through the URL, known as URL
rewriting. When URL rewriting is used, links that contain the same session
identifier restore the same session, even if opened in a new tab.

session标识也可以通过URL传递,即URL重写。当URL重写时,同一session标识符保存同一session,开新标签也是这样。

Consider the use case of updating a golf course record. Assume that the golf course is
stored in the HTTP session while it’s being modified. If you select a course to modify in
one browser tab and then select a different course to modify in a second tab, the second
course selection on the server overwrites the first. When you click Save in the first
tab, assuming the changes are applied directly to the record in the session, you inadvertently
modify the second course instance. Things get even trickier if you’re working
with a multipage wizard, since the leakage of data can be less apparent. Yet another
problem is that data in the session isn’t protected from concurrent use, so if two
requests try to access session data simultaneously, it can lead to a race condition.
The most severe problem with the session scope is that it is mostly unmanaged. If
objects continue to build up in the session, and there’s no application-provided garbage
collector to clean it out, memory leaks that impact the performance of the application
will occur, thus affecting all of the users.
It’s possible to work around the aforementioned problems by using the session
with care or by putting in synchronization and locking code to prevent collisions, but
that’s a burden on you as a developer. In general, heavy use of the session scope is a
common source of bugs, and the unexpected behavior it causes is often difficult to
reproduce in a test environment.

考虑升级高尔夫课程的用例,假定在被修改时,课程是保存在HTTP session中,如要在一个标签中选了一个课程进行修改,又在第二个标签中修改,则第二个中的修改会覆盖第一个的,保存第一个则盖掉第二个。如果是通过多页向导的方式,则情况更糟。因为这样数据泄漏更不易察觉。另一个问题是session内的数据不受保护。如果两个请求同时访问session数据,则会产生竞赛冲突。
最严重的问题还是session scope内基本是无管理的。如果session中不断建立对象,应用程序又没有提供垃圾回收器,影响性能的内存泄漏就会出现,从而影响所有用户。
可以能过小心及放入同步及加锁代码的方式来绕开这一问题,但这些负担都交给了开发者。总之,过度地使用session scope是BUG出现在主要根源,并用在测试环境这些问题还不易重现。


NOTE
Cookies have the combined problem of request parameters and session
data. They only store string data, capped at a fixed size (~4K), and they
cannot be partitioned by use case. Their utility is in identifying a repeat
visitor or storing basic user preferences.

注:
Cookies 同时有请求参数和session数据这两个问题。只保存串数据,限制在固定大小(4k),不能被用例分割使用,他们的作用是标志重复的访问者或保存基本的用户资料。

Although the existing storage options are workable, they aren’t well suited for maintaining
an isolated working set of data for a use case. Clearly there is room for a better
solution. Surprisingly enough, that solution lies in the HTTP session. Despite my having
just bashed the session for its weaknesses, it’s not all bad. It simply needs to be partitioned
and better managed. That’s exactly what Seam does. The conversation
context is designed to be a well-behaved abstraction layer over the HTTP session.

虽然现存的选项是可用的,但不能很好的适合于维护一个独立的用户例用户集。很显然可以有更好的解决办法。让人吃惊的是,方案本身还是依赖于HTTP session。虽然我刚损了它一通,但应该说,它还不是一无是处。它只是需要被区分开并被更好地管理。SEAM是在HTTP session上增加了一个抽象层。

7.2 The conversation context
The conversation context is one of two Seam contexts introduced to serve businessworld
time frames as opposed to servlet life cycles (the other is the business process
context). From reading the previous section, you should have a clear picture as to a
conversation’s purpose. In this section, you’ll learn how it’s maintained.

会话上下文
会话上下文是SEAM的两个上下文之一,另一个是业务过程上下文。

7.2.1 Carving a workspace out of the HTTP session
The conversation context is carved out of
the HTTP session to form an isolated and
managed memory segment, as illustrated in
figure 7.3. Seam uses the conversation context
as the home for a working set of context
variables.
You may shudder at the mention of using
the HTTP session to store the conversation,
given the problems cited in the last section.
However, a conversation doesn’t suffer from
the same problems as its parentage. First and
foremost, the lifespan of a typical conversation
is on the order of minutes, whereas a session
can last on the order of hours. This
difference is made possible by the fact that a
conversation has its own distinct life cycle,

切分HTTP session。
会话上下文是切分HTTP session,以形成一个独立的受管理的内存片。SEAM用会话上下文,上下文变量工作集的家。
你也许会奇怪于使用HTTP session去保存会话。不管怎样,会话没有经历其祖辈的问题。最重要的,生命周期延长了,会话有由SEAM管理的自己的生命周期了。

which Seam manages. Each conversation can have its own timeout period, which
defaults to the global timeout setting, covered in section 7.3.5. Additionally, concurrent
conversations are kept isolated, unlike session attributes, which just get jammed
together in a single map.
Since conversations are stored in the session, two requirements must be met:
■ Conversation-scoped components must implement java.io.Serializable.
■ The session timeout, defined in web.xml, must exceed all conversation timeouts.
A conversation has a clear set of life-cycle boundaries that coincide with the boundaries
of a use case. When the user triggers a condition that begins a conversation, a new
managed area of the HTTP session is sectioned off and dedicated to that conversation.
A unique identifier, known as the conversation id, is generated and associated with this
region of the session. The conversation id is passed on to the next request as a request
parameter, hidden form field, or JSF view root attribute. Fortunately, propagation of
the conversation id is handled transparently in a Seam application. As a result of the
conversation id and session token being sent together to the server, the conversation
is retrieved from the session and associated with the request.

另外,同时发生的会话各自独立。
既然会话存储于session,两个需求必须实现。
Conversation-scoped范围的部件必须实现序列化
定义在web.xml中的session timeout,长度必须超过所有conversation的。


会话有着清晰对应用例的生命周期。当用户触发一个会话,一个新的HTTP session管理区建立起来并交付给会话,具有一个独立的标识符conversation id。可以在下次请求中作为参数、隐含域或JSF view的根属性。SEAM的这种传播是完全透明的。
当conversation id会话及session token被一起送到服务器,会话是可以从ession中检索到,并关联到请求中。

NOTE
Although the HTTP session is used as the storage mechanism for a conversation,
understand that the memory footprint is strikingly low because
conversations are aggressively managed. There’s no chance of them lingering
on to cause memory leaks.

虽然HTTP session是会话的存储机制,不用理解内存需求,因为会话是被严格管理的,不会有内存泄漏问题。

The conversation context is an ideal place to store data that’s needed over the extent
of several pages. It leverages the session’s ability to store arbitrarily complex objects
while preserving object identity, but doesn’t suffer from memory leaks or concurrency
problems. What makes conversations truly unique is that they remain isolated from
one another.

在有多页的情况下,会话上下文是理想的保存数据的地方。

SOLVING THE MULTIWINDOW CONCURRENCY PROBLEM
Let’s revisit the scenario in which different golf course records are being edited in separate
browser tabs. This time around, we’ll assume that a conversation is being used to
manage each use case. The user begins by selecting a golf course in the first tab, which
starts a new conversation and presents the user with an update form. The user then
switches to the second tab and selects a different course, again resulting in a conversation
being created and the rendering of an update form. Each tab now has its own
conversation. The user then switches back to the first tab and clicks Save. The form
values from that tab are sent to the server along with the conversation id. On the
server, the conversation context is retrieved from the session using the conversation
id. The course instance is pulled out of that conversation, the form values are applied
to it, and finally it’s synchronized to the database.
Although the two tabs are serving the same use case, with the same context variables,
the data is kept isolated. Conversations don’t suffer from leaky behavior because they
aren’t shared across all tabs and windows like the session. Instead, a conversation can be
reserved for a single tab and restored on each request by passing the conversation id.
As such, activity occurring in one tab doesn’t affect other tabs (that use different
conversations). Although conversations prevent unwanted sharing of data between separate
use cases, sharing data across multiple requests in the same use case is a desirable
feature of the conversation.

解决多窗口同步的问题
回到前面的多个标签同时修改一个高尔夫课程的问题。这次我们假定用会话来管理每个用例。用户在一个标签中选择一个课程,这就开始了一个会话,呈现给用户一个更新页面。用户切换到另一个标签页选择另一个课程。同样,会打开另一个会话,呈现表单。每个标签有自己的会话。用户随后返回第一页并保存。依照conversation id传到了服务器,找到了对应的会话上下文,课程实例被从会话中提取出来,最终同步到数据库。
虽然两个标签服务于同一个用例,同一个上下文变量,数据还是独立的。会话不会有泄露,因为没有像session那样共享多个标签或窗口。
事实上,每个标签有自己的会话,这样,各标签的行为不会互相影响。

A BUSINESS TIER CACHE
The conversation context provides a natural caching mechanism that’s readily controlled
from the application, allowing cached data to be relinquished or refreshed in
accordance with the business logic. You can even provide the user with controls that
force the data to be refreshed on demand. If the conversation is abandoned, it’s not long
before this state is cleaned up by Seam (unlike with state stored in the HTTP session).

会话上下文提供了一个天生的可以容易地受应用控制的缓冲机制,允许缓冲的数据可以依照业务逻辑被放弃或刷新。你甚至可以给用户控制权来手动刷新。
如果会话被放弃,SEAM给快会清理这个状态。

Caching data is critical because it avoids redundant data inquiries. If you cache
database result sets, it means that you don’t have to consult the database again when
there’s no expectation that the data has changed. You should take advantage of this
opportunity because, of all the tiers in your application, the database tier is the least
scalable. Don’t abuse the database by repeatedly asking it to retrieve the same data
over and over again. When the application fails to track data that it was already fed, it
hurts the performance of both the database and the application.

缓冲数据很重要,因为可以避免冗余的数据查询。如果你缓冲数据库结果集,如果数据不常有变化的情况下,则不用去查数据库。你就利用这个便利,因为你应用程序的所有层中,数据库层是最不可扩展的。所以不要总是去查询同样的数据。那会同时伤害到数据库及应用程序的性能。


Reducing load on the database is one of the primary concerns of an ORM. An ORM
supports two levels of caching. The first-level cache, known as the persistence context,
holds the collection of all entities retrieved by a single persistence manager, which
you’ll learn about in chapters 8 and 9. If the persistence manager is scoped to the conversation,
then the ORM works naturally to reduce database load.

ORM的一个主要目标就是减少对数据库的加载。一个ORM支持两级缓冲,第一级是持久上下文。由一个单一的持久管理器来检索所有的实体。第8、9章你会学到。如果将实体管理器的范围定在会话级别,则ORM就很自然地减少了数据库的加载。

The second-level ORM cache is shared by the persistence managers and holds persistent
objects previously loaded through any persistence context. It is used as a way to
reduce traffic between the application and the database. Employing an intelligent
algorithm, it attempts to keep the cache in sync with the changes made to the database.
However, regardless of how good this logic is, within the scope of a use case, it
lacks the business-level insight to know exactly when data should be considered stale.
Expecting the second-level cache to make up for the application’s inability to retain
data is a misuse of the technology.

第二级ORM缓冲是被实体管理器共享的。前面持久上下文加载进来的持久化实体都在这里。使用精明的算法,尽是使其与数据库有好的协同。但不管怎样,在用例范围内,缺少业务级的感觉,无法得知何时数据应被认为过时。所以指望二级级冲来装点应用程序的不足,只能是对技术的乱用。

The need for a stateful context acting as an intermediary between the browser and
the database is especially important in the world of Web 2.0, where Ajax requests are
sent to the server at a rate that far exceeds the previous usage pattern of web applications.
Requests that leverage the conversation context save database hits and are faster
since they’re returning data that’s held close at hand. The conversation plays another
important role in Ajax: preventing the requests from accessing data concurrently.


在浏览器和数据库间加一个中间层在WEB2.0世界很重要。在数据中,AJAX的请求远超过了前辈。会话上下文保存数据库点击,因为返回手边的数据,所以速度快。对于AJAX,会话起着另一个重要的作用就是防止同步地访问数据。


PREVENTING CONCURRENCY PROBLEMS
Seam serializes concurrent requests that access the same conversation. This means only
one thread is allowed to access a conversation at any given time. In pre–Web 2.0 applications,
this might help deal with a double submit, but when Ajax starts firing off
requests like they are going out of business, the likelihood of your data entering an
inconsistent state as a result of concurrent access dramatically increases. Seam keeps
those Ajax requests in line, so you can be confident that conversation-scoped data won’t
be modified by a second incoming request while the first request is being served.

Conversations fit very naturally with Ajax. The combination of serialized access
and stateful behavior drastically minimizes the risk of using Ajax in your application.
With these mechanisms in place, you can rest assured that performance and data consistency
won’t suffer. You’ll learn more about how well Seam and Ajax fit together in
chapter 12.

Having explored ways in which the conversation context solves the need for a userfocused
stateful context, let’s examine the types of data you might typically store in a
conversation as you prepare to use it.

SAEM序列化并发请求来访问同一会话。这意味同一时刻只有一个纯种可以访问会话。在WEB2.0以前,可以用处理提交的方法解决。AJAX的出现让你的数据的状态不一致的情况大大增加,因为会有很多的并发情况。SEAM使这些AJAX再次排队,可以保证前面的会话数据不会被第二个请求改掉。

会话与AJAX的结合很自然完美。序列化的访问和有状态行为极大的降低了AJAX在应用中可能发生的危险。在12章细述。


7.2.2 What you might store in a conversation
The conversation provides a way for data to be stashed away during user “think”
time—the time after the response is sent to the browser but before the user activates a
link or submits a form. Additional information is accumulated in the conversation as
the user moves from screen to screen. There are four classifications of data that a
working set is used to store, all of which are demonstrated in this chapter:

在会话中你打算存储什么
在数据发到用户浏览器和用户提交回数据前,会话会话提供了一种方法来收藏好数据。附加的信息,如用户在标签或窗口中换来换去,会累积在会话中。
有四种数据存储方式:

■ Nonpersistent data —An example of nonpersistent data is a set of search criteria
or a collection of record identifiers. The user can establish the state in one
request and then use it to retrieve data in the next. This category also includes
configuration data (such as the page flow definition).
■ Transient entity data —A transient entity instance may be built and populated as
part of a wizard. Once the wizard is complete, the entity instance is drawn from
the working set and persisted.
■ Managed entity data —The working set provides an ideal way to work with database-
managed entity data for the purpose of updating its fields. The entity
instance is placed into the working set and then overlaid on a form. When the
user submits the form, the form values are applied to the entity instance that’s
stored in the working set (whose object identity has been preserved) and the
changes are flushed to the database transparently.
■ Resource sessions —The conversation context offers an ideal mechanism for
maintaining a reference to an enterprise resource. For instance, the persistence
context (a JPA EntityManager or Hibernate Session) can be stored in
the conversation to prevent entity instances from becoming detached prematurely.
The next several chapters focus on how conversations benefit persistence
management.
In this section you learned what we mean when we say conversation: a context for keeping
data in scope for the duration of a use case and a means of enabling stateful
behavior in your applications. The next step is to learn about the conversation life
cycle and how to control conversations by defining conversation boundaries.

1、不存,一个例子是搜索criteria或记录标识集,用户可以在一个请求中建立状态而在下一个时使用。这一类别也包括配置数据。(如页流定义)
2、Transient实体数据,可以作为向导的中间状态,向导结束,只有实体实例会提取出并保存。
3、被管理的实体数据,工作集提供了一种理想的方式来操作数据库管理的实体数据。整个实例放入工作集,并附于表单。当用户提交表单,表单的值被应用到实例实例。(可以做到这一点是因为其对象标识符已保存),变化的数据透明地存入数据库。
4、Resource sessions,会话上下文提供了一个理想的机制可以维护一个企业资源的参考。如持久化上下文(JPA EntityManager or Hibernate Session)可以存放在会话中,以防止实体实例过早地脱离。

这一节你会知道会话的含义。是一个用例中保存数据的上下文,是让你的应用有状态的方法。

7.3 Establishing conversation boundaries
The conversation context is unique from other Seam contexts you have used so far in
that it has explicit boundaries dictated by application logic, as opposed to implicit
boundaries that correlate with a demarcation in the servlet or JSF life cycle. The
boundaries of the conversation context are controlled using conversation propagation
directives. This section introduces these directives and demonstrates how they’re
used to transition the state of a conversation and effectively manage its life cycle.

建立会话边界
到目前为止你所用的其它SEAM上下文都有明确的边界,而不是servlet or JSF life cycle的隐含的边界。会话的边界由会话传播指令控制。本节就介绍。


7.3.1 A conversation’s state
A conversation actually has two states: temporary and long-running. There is also a
third state, nested, which is a characteristic of the long-running state. Nested conversations
are covered in section 7.4.2. Right now, I want to focus on the first two states.
Switching the state of a conversation is referred to as conversation propagation. When
you set the boundaries of a conversation using the conversation propagation directives,
you’re not initiating and destroying the conversation, but rather transitioning it
between a temporary and long-running state.

会话实际有两种状态,临时的和长期运行的。其实还有一种嵌套的,是长期运行的一种特性。嵌套的在7.4.2中再说。先说说前两种。
会话状态的切换也叫会话传播。当你用会话传播指令设定了会话的边界后,你不是在初始化或销毁会话,而是在在做临时与长期状态的切换。

TEMPORARY VS. LONG-RUNNING CONVERSATIONS
Most of the time, when people talk about Seam conversations, they’re referring to
long-running conversations. The discussion in the early part of this chapter pertains to
long-running conversations. A long-running conversation remains active over a series
of requests in correlation with the transfer of the conversation id. In the absence of a
long-running conversation, Seam creates a temporary conversation to serve the current
request. A temporary conversation is initialized immediately following the Restore View
phase of the JSF life cycle and is destroyed after the Render Response phase.

临时对长期
人们说的会话一般都是在指长期。在没有长期会话的时候,SEAM使用临时的会话。在JSF life cycle的Restore View phase之后,临时的会话会立刻初始化并在Render Response phase之后立刻销毁。

You can think of a temporary conversation as achieving the same result as the flash
hash in Ruby on Rails: transporting data across a redirect. In Seam, the temporary conversation
carries conversation-scoped context variables across a redirect that may occur
during a JSF navigation event. This works by maintaining the temporary conversation
until the redirect is complete. So to clarify, a temporary conversation is destroyed after
the Render Response phase ends, even if it’s preceded by a redirect. The most popular use
of a temporary conversation is to keep JSF messages alive during the redirect-after-post
pattern, assuming those messages are registered using Seam’s built-in, conversationscoped
FacesMessages component.

你可以将临时会话想像成两只小蜜蜂。。。
重定向时传送数据。在JSF导航事件中,SEAM临时会话在重定向过程中,带着会话范围的上下文变量。Render Response phase结束后,临时会话就会销毁,即使是在重定向中处理的。最常用的使用临时会话是JSF messages保持活动。在redirect-after-post模式,假定信息是使用Seam自带的会话范围的FacesMessages部件注册的。


The other purpose of a temporary conversation is to serve as a seed for a longrunning
conversation. A long-running conversation is nothing more than a temporary
conversation whose termination has been postponed. This postponement lasts from
the time the begin conversation directive is encountered up until the end conversation
directive is met. Instead of just surviving a navigation redirect, a long-running conversation
is capable of surviving a whole series of user interactions. Only when the conversation
reacquires the temporary state is it scheduled to be terminated. In Seam,
every request is part of a conversation. You just have to decide how long you want that
conversation to last.

另一个临时会话的目的是对于长期运行的会话,做为服务的种子。长期会话不比临时会话多个什么,只是终结点向后推延了。整个地经过了begin conversation到end conversation指令的全过程。 不单单维持导航的重定向,长期运行的会话可以伴随整个用户的交互。
在SEAM中,每个请求都是会话的一部分,你必须决定会话持续的时间。


CONVERSATION PROPAGATION DIRECTIVES
Learning to use long-running conversations involves learning the conversation propagation
directives, listed in table 7.1, and how they transform a temporary conversation
to and from a long-running conversation. You can think of conversation propagation

会话传播指令


directives serving a parallel purpose for a conversation as transaction propagation
directives do for a transaction.
The conversation propagation directives can be applied using the following
means:
■ Method-level annotations
■ UI component tags
■ Seam page descriptor
■ Seam conversation API
■ Stateful page flow descriptor (end conversation only)

会话传播指令可以应用下面的方式:
方法级注释
UI部件标签
SEAM页描述符
SEAM会话API
有状态页流描述符

These variants are provided to accommodate different usage and design scenarios,
allowing you to establish conversation boundaries where it makes the most sense in
your application. You’ll learn to use the conversation propagation directives in the
next section.
The conversation propagation directives dictate the life cycle of the conversation.
Figure 7.4 diagrams this life cycle, showing how the state of the conversation changes
during the request as a result of encountering a conversation propagation directive.

各变化是为了适应不同的使用和设计场景,让你建立会话边界,这对你的应用很有用。下一节你会学到使用会话传播指令。
会话传播指令指示会话的生命周期。图7.4示例了这个生命周期。显示了会话的状态是在请求间遇到会话传播指令时是怎样改变的。


Let’s step through the diagram in figure 7.4. At the start of the request, a longrunning
conversation is restored if the conversation id is detected among the request
parameters. If the conversation id is absent or invalid, Seam initiates a new, temporary
conversation. At any point during the processing of the request, the conversation may
change state as the result of encountering a conversation propagation directive. The
begin directive transitions a temporary conversation to long-running. The join directive
has the same effect as begin, except that it can enter into an existing long-running
conversation, whereas the begin directive raises an exception in this case. The nest
directive can also begin a long-running conversation, but if one exists, a new conversation
is created, temporarily suspending the existing one. The end directive sends the
long-running conversation back to its temporary state. At the end of the request, the
temporary conversation is destroyed, whereas the long-running conversation is tucked
away in the HTTP session to be retrieved by a subsequent request. Although not
shown, if the conversation being restored is invalid or has previously timed out, the
user is notified and forwarded to a fallback page if one is configured.


来穿越一下图7.4的示例图。在请求的起始,如果在请求参数中检测到会话ID,一个长期的会话被存储。如果没有此ID或无效,SEAM初始化一个新的、临时会话。在处理请求期间的任一点,当遇到会话传播指令,会话会改变状态。开始指令会将一个临时会话变成长期运行,因此开始指令在这种情况下会抛出异常。嵌套指令也可以开启长期会话。如果已有一个,则一个新的会话会被建立,原有的先挂起。结束指令使长期运行的会话回到临时状态。在请求末尾,临时会话被破坏,而长期运行的会话放入HTTP session,留待由后续请求检索。虽然没有明示,如果被存储的会话无效,或已超时,用户会被通知,如果配置了,还可以被导向故障页面。

Seam uses a built-in component to keep track of the conversation’s state, including
its relationships to nested and parent conversations. It’s important to know that this
component exists as you’ll often find that you need to reference it.

SEAM使用内建的部件来跟踪会话状态。包括嵌套及父会话关系。知道部件存在很重要,因为要经常引用它。

THE CONVERSATION COMPONENT
Seam maintains the state of the conversation in an instance of the built-in conversationscoped
component named conversation. This component provides a wealth of information
about the current conversation in the form of properties and exposes methods
for acting on the conversation. These properties and methods are listed in table 7.2.

会话部件
SEAM用内建的会话范围的部件conversation来维护会话状态,这个部件提供当前会话表单属性和暴露方法中的丰富信息。这些方法和属性列于表7.2

The most important property on the conversation component is the conversation
id, which is typically accessed using the value expression #{conversation.id}. The
conversation id is used to restore the conversation from the session at the beginning
of a request. You’ll see this expression used often in this chapter. The properties on
the conversation component aid in making decisions about navigation or rendering

会话部件中最重要的属性是conversation id,用#{conversation.id}访问。conversation id用于在请求开始时,从session保存会话。你会发现这个表达式在本章很常见。会话部件的属性帮助建立关于导航和重画的决定。


会话部件的方法可改变会话的状态,经常用于page actions或链接或按钮的action方法

有了这些部件,你现在可以学习怎样定义会话边界。当你读完后面的几节,我建议你学习定义边界的几个选择。使用最爱或最流行的。因为你可以用很多方法定义每个边界并不意味你应该使用所有的,至少没必要。

7.3.2 Beginning a long-running conversation
To demonstrate the use of a long-running conversation, let’s work through an example
of a multipage wizard that captures information about a golf course and adds it to
the Open 18 directory. Entering data for a golf course can be quite intimidating, so a
wizard is used to break up the form into short, logical steps, shown in figure 7.5. Each
box in figure 7.5 represents a screen with a form to fill out. As the user moves from
screen to screen, the information from previous screens must be accumulated and
stored so that it’s available when the final screen is complete and the course is persisted
to the database.

开始一个长期会话
要示范使用一个长期运行的会话,让我们用一个多页面向导例子,获取高尔夫课程的信息并将其增加到Open 18目录。向高尔夫课程录入信息很长,所以向导将其分成多个短的、逻辑的步骤。如图7.5。图中每个方块代表一个带表单的页面。

The textbook choice for starting conversations such as the golf course wizard is to add
the @Begin annotation to the action method that spawns the wizard. However, there
may be times when you need to start a conversation from a GET request, in which case
either the page descriptor tag or a UI component tag is a
more appropriate choice. The latter two may be attractive to you if you prefer to keep
your navigation controls out of Java code or you need more fine-grained control over
the conversation boundaries. As you read through this section, keep in mind that you
need to begin the conversation only once, so these options are mutually exclusive.
Let’s start by looking at how to use the @Begin annotation.

开始会话如高尔夫课程向导是向action方法增加@Begin。有很多次,你需要从GET请求开始会话,这种情况下,页面描述标签或UI部件标签是更好的选择。如果你想让导航规则分离于JAVA代码之外或希望在会话边界有细粒度的控制,后两个更好。在读本节时,要记住开始会话只须一次,所以这些选项是手动执行的。让我们看看怎样使用@Begin。

AN ANNOTATION-BASED APPROACH
One of the method interceptors that Seam wraps around components is the ConversationInterceptor.
This interceptor looks for the @Begin annotation on the component
method that’s being invoked. If it finds one, it converts the temporary conversation
to long-running if the method completes successfully. See the accompanying sidebar.

基于ANNOTATION的方法
一个SEAM包装部件的方法拦截器是ConversationInterceptor。它在触发的部件方法上找@Begin,如果找到,在方法成功完成,则转换临时会话为长期运行的。

Seam’s definition of success
For many of Seam’s annotations that designate an action to be performed, such as
@Begin and @End, Seam only performs the action if the method completes successfully
according to Seam’s definition of success. For Seam to consider a method call
successful, it must return without throwing an exception and it must return a non-null
value unless it's a void method.

SEAM所指的成功
很多SEAM的annotations代表action的执行,如@Begin and @End。但只在方法完全成功才执行。SEAM所指的成功是有返回,且不抛异常,必须返回一个非空的值,除非它是个空方法。

The @Begin annotation is outlined in table 7.3. The pageflow attribute is used to initiate
a stateful page flow and is covered in section 7.6. The flushMode attribute is used
to switch the flush mode of the persistence context when the conversation begins and
can be used to initiate an application transaction as covered in chapter 9.

@Begin在表7.3中简述。pageflow属性被用于初始化一个有状态页面流,这在7.6节讲。flushMode属性用于以会话开始并可用于初始化一个应用事务时,切换存储上下文的flush模式。见第9章。

Let’s use the @Begin annotation to initiate a long-running conversation for the course
wizard by placing it on the addCourse() method. This method is invoked at the start of
the course wizard, beginning a long-running conversation. It also outjects the course
property into the conversation context, making it available throughout the wizard:

让我们来向addCourse()方法加@Begin来初始化一个课程向导的长期运行的会话。这人方法在课程向导开始时被触发,开始一个长期会话。它也注出课程属性到会话上下文,使其在整个过程中有效。


要开始课程向导,用户导航到设施的细节页面,然后点击命令按钮调用addCourse()方法并传递设施的ID。



The following navigation rule is defined to advance the user to the first screen in the
wizard:

下列导航规则导航用户到向导的第一页面。






The @Begin annotation isn’t limited to action methods. You can also add it to a method
used as a page action; combine it with a life-cycle annotation, such as @Create; or tie it
to a @Factory method. These options allow the long-running conversation to start at various
points in the Seam life cycle. Remember, it doesn’t matter when the state of the conversation
changes during the request, but rather what the state of the conversation is
when the request is complete.

@Begin不只限于action方法,你也可将其加到一个方法用于page action,与结合生命周期annotation结合使用,如@Create,或与@Factory方法绑定。这些方法允许一个长期运行的会话在请求过程中的SEAM生命周期的多个点开始。


Instead of using a UI command button to activate the addCourse() method, you
could trigger this method by registering it as a page action on the opening screen in
the wizard:

不是使用UI部件按钮来激活addCourse()方法,你可以在开始页面将其注册到page action。

action="#{courseWizard.addCourse}"/>

In this case, when the servlet path /coursewizard/basicCourseInfo.seam?
facilityId=3 is requested in the browser, the addCourse() method is invoked and a
long-running conversation is started. The benefit of using a page action is that it can
start a long-running conversation from a bookmarked page or direct link, rather
than waiting for a JSF postback. Starting a conversation from a page request may
require additional logic either to prevent excessive conversation creation or to
enable conversation joining, both of which are covered later on.

这种情况,当servlet 路径/coursewizard/basicCourseInfo.seam?facilityId=3在浏览器中请求,addCourse()方法被激活,一个长期会话开始。使用page action的好处是可以从书签页或直接的链接开始一个长期会话,而不是要等到一个JSF postback。从一个页面请求开始一个会话可能需要附加的逻辑,来防止过度会话建立或启用会话合并,这两个后面再讲。

You also have the option of defining a factory method for the course context variable.
When the course context variable is looked up by the first screen in the wizard
through a factory, a long-running conversation can be started:

你也可以对course上下文变量定义一个工厂方法。当course上下文变量被向导第一页面的工厂looked up,一个长期运行的会话会启动。

@Begin @Factory("course")
public void initCourse() {
course = new Course();
course.setFacility(entityManager.find(Facility.class, facilityId));
}

Factory methods are a good place to start conversations because they don’t require
user interaction, nor do they require XML to be defined in the page descriptor. The
same goes for the @Create life-cycle method. You can also have the conversation begin
the very first time the CourseWizard component is accessed:

工厂方法是个启动会话的好地方,因为它们不需要用户交互,也不需要XML定义页面描述文件。这也适用于@Create生命周期方法。你也可以让会话开始于CourseWizard部件的第一次访问。

@Begin @Create
public Course initCourse() {
course = new Course();
course.setFacility(entityManager.find(Facility.class, facilityId));
}

If you fancy those angled brackets, you might instead find the page descriptor to be an
ideal place to begin a long-running conversation, which we look at next.

如果你喜欢,你可以用页面描述文件来开始长期运行的会话,后面再说。

A PAGE-ORIENTED APPROACH
One way to start a long-running conversation in a page-oriented manner is by using a
@Begin method as a page action. However, the task of starting a long-running conversation
when a page is requested is so common that Seam includes two built-in options.
You can either use the method-binding expression #{conversation.begin} in a page
action or nest the page descriptor tag with a node.
The tag can also be used during a page transition.

面向页面的方法
一个面向页面的开始长期会话的方式是使用@Begin方法作为page action。当页面被请求,开始长期会话任务。这很常用,SEAM含有两个内建选择,你可以使用方法绑定表达式于page action或嵌套页面描述文件标签于节点。标签也可以在页面切换时使用。

Let’s start by applying the tag to a page transition. We’ll
assume that the command button shown earlier is activated, but the action method
doesn’t have the @Begin annotation. Instead, the following navigation rule will start a
long-running conversation, then direct the user to the first screen in the wizard:

让我们从应用标签开始页面切换。我们假定前面那个命令按钮按下,但action方法没有@Begin。而是用导航规则启动长期会话。然后导航用户到向导的第一页面。






If you want to preclude the use of the command button, you can instead declare the
long-running conversation to begin when the /coursewizard/basicCourseInfo.xhtml
view ID is requested. This is done either by using the begin() method on the built-in
conversation component:

如果你想阻止用户使用命令按钮,你可以在/coursewizard/basicCourseInfo.xhtml 的view ID被请求时,不让长期运行运行。可以使用begin()方法于内建的会话部件。

action="#{conversation.begin}"/>

or by nesting the tag directly inside the node:

或嵌套于节点。



If a fine-grained page descriptor is associated with the first screen of the wizard,
/coursewizard/basicCourseInfo.page.xml, you could exclude the view-id attribute,
thus simplifying the declaration to

如果一个细粒度的页面描述文件关联到向导的第一页面,/coursewizard/basicCourseInfo.page.xml,你可以排除view-id属性,简化声明如:



The only downside of using the built-in page action alone is that you can’t perform
“prep work” in a component method before the first page is rendered. The
addCourse() method, for instance, initializes and outjects the course context variable.
The built-in page action is best suited for simply “turning on” a conversation.

单独使用内建的page action唯一的不便是你不能在第一个页面生成前,在一个部件方法中做“prep work”,如addCourse(),实例化和注出course上下文变量。内建的page action是最适合简单的启动会话的方法。

The page descriptor offers a lot of control for defining conversation boundaries
because you can distinguish between initial requests, postbacks, and even action
method outcomes. However, you may want to be able to associate the start of a longrunning
conversation with a link or button directly. For that, you can use any of the tags
from Seam’s JSF component library to control the boundaries of the conversation.

页面描述文件提供了很多控制来定义会话边界,因为你可以区分initial requests, postbacks, and even action
method outcomes。你可能想用链接或按钮直接关联一个长期运行的会话。对此,你可以使用任何SEAM的JSF部件库的标签来控制会话的边界。

A UI COMPONENT TAG APPROACH
As a third option, you can begin a long-running conversation using one of Seam’s UI
component tags. You can either enhance an existing UI command component with conversation
propagation capabilities by adding a nested
tag, or you can use one of Seam’s command components,

UI部件标签方法
作为第三种选择,你可以使用一个SEAM的UI部件标签开始一个长期会话。你也可以用部件传播能力扩充一个现有的UI部件,强加一个嵌套的标签。或者你可以用一个SEAM的命令部件

Seam reads a request parameter named conversationPropagation to decide how
to address the current conversation. This request parameter is passed either through
the query string of the URL or in the POST data. Rather than having to add this parameter
yourself, you can use the component tags cited here to add it for you, which
abstracts away the name so that it’s not hard-coded in your source code. If you need to
use a custom UICommand component or submit a JSF form when starting the conversation,
you can add a nested tag to any UI command component
tag to give it conversation propagation abilities. Let’s assume the facility list page
includes a link in each row to start the course wizard for that facility (citing a slightly different
use case here), passing the iteration variable, _facility, as a parameter:

SEAM读请求名为conversationPropagation的参数来决定怎样定位当前会话。这个请求参数或者通过URL请求串,或者在POST数据中被传送。不是一定要你自己加这个参数,你可以使用部件标签来为你加,它不会向你的源代码中硬编码。如果你需要自定义的UICommand部件或在开始会话时提交一个JSF表单,你可以增加一个嵌套的标签到任何UI命令部件标签给其会话传播能力。让我们假定设施列表页包括一个链接到每列,以开始设施的课程向导,传递迭代变量_facility作为参数。

value="Add course...">

Instead of using the tag as a nested element in a regular
JSF component tag, you can use the Seam command tags to specify the conversation
propagation using the propagation attribute:

不是在常规JSF部件标签中使用作为嵌套元素,你可以用propagation属性,使用SEAM命令标签来指定会话传播

propagation="begin" value="Add course..."/>

In the event that you don’t need to submit a form, the command tags are a great way to
work with conversations because they have propagation controls built right in and they
automatically pass along the conversation token. That adds to the previously mentioned
benefits of the command tags to produce bookmarkable URLs (covered in chapter 3).

在你不需要提交表单的事件,命令标签是与会话工作的很好的办法,因为其自带传播控制,并自动传递会话令牌。这增加了前面提到的命令标签的益处,可生成书签URL(见第3章)。

If you’ve been experimenting with the begin directive while reading this section,
you may have encountered an error message reporting that a long-running conversation
can’t be created because one is already active. To remedy this problem, you need
to know about conversation joining and how to enable it.

如果你实验过begin指令,你可能遇到一个错误信息报告“因为已有一个存在,一个长期运行会话不能被建立”。为了解决这个问题,你应该知道会话合并及怎样启用。

ENABLING CONVERSATION JOINING
By default, Seam only begins a long-running conversation if one isn’t already in play. A
long-running conversation is active if it was restored from a previous request or if a
begin directive has already been encountered. In either case, if an attempt to cross the
begin threshold happens again in the same request, Seam throws an exception. Th

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/21802202/viewspace-1023804/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/21802202/viewspace-1023804/

你可能感兴趣的:(ui,数据库,操作系统)