Release Notes

Project Documentation

Foundation

The Conversation Scope Problem

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", "workflow" or
"dialog"), during which the same java objects need to be kept in memory.

However JSF provides only three scopes for data to be stored in:

Application scope

Session scope

Request scope

Application scope is only rarely of use; such data is shared across all users of that JSF application.
Request scope is not useful for the above scenario; all data stored in request scope is discarded at
the end of each "command".

Session scope can be used to hold data across multiple requests (a conversation),
but suffers from a number of other issues:

When the conversation is complete, it is best to discard all the related objects in order
to save memory. However this is quite difficult to do when using session scope.

When a user performs some task for a second time (eg purchasing a second insurance policy),
it is usually better for the backing beans to be new instances rather than having whatever
state they had at the end of the previous pass. This is difficult to achieve when the beans
are in session scope; every relevant bean needs to be explicitly deleted or reset. However
when these objects are stored in a conversation this happens automatically as the conversation
(with all its beans) has been discarded.

The user cannot have multiple windows open on the same site. Sessions are typically
tracked using cookies, and all windows associated with the same browser instance
share cookies and therefore are within the same "session". If two windows are created
for the same session then very strange effects can be caused due to the same "backing beans"
being used by the two windows. Note that JSF implementations generally provide support for
multiple concurrent windows (eg MyFaces and Sun RI) but this only means that the JSF
*components* are safe for use with multiple windows; any application that uses only
request-scope beans will therefore work correctly but apps with session-scoped beans
will still suffer confusion.

The Tomahawk library provides a partial solution to this conversation problem with
the t:saveState tag, which allows data to be bound to a JSF View; it is then
available across multiple consecutive requests to the same view. It can also be
"passed" to a following view when navigation occurs. However this can be difficult
to use, as every bean that needs to be part of the conversation needs to be explicitly
tracked.

The draft JSF 2.0 specification currently defines a new "view" scope which also
provides a partial solution to this issue that is similar to the Tomahawk
t:saveState tag.

The Orchestra library provides another alternative.
This solution works across all JSF implementations (particularly Apache MyFaces and
the Sun Reference Implementation). It works for Java 1.4 or later. If java1.5 is
being used then custom annotations are available to make its use even easier.

Orchestra does require conversation-scoped managed beans to be declared via a good
dependency-injection (aka IOC) framework with AOP support. The standard JSF
managed-beans facility does not provide sufficient flexibility. While it should be
possible for Orchestra to be integrated with any appropriate such framework it
initially supports only Spring 2.x. This is no great drawback as there are many
other good reasons to use Spring! In the remainder of this document we shall
assume Spring is the dependency-injection framework being used.

Various other projects (JBoss Seam, Apache Shale Dialogs, Spring WebFlow) provide
conversation/dialog support that is similar to Orchestra. See the Orchestra wiki pages
for up-to-date comparisons of Orchestra with other projects.

Orchestra Conversation Scope Features

The normal behaviour for JSF is that when an EL expression references a
bean that cannot be found anywhere in the current scopes, the managed bean
declarations are searched for the specified name. If a match is found then
the bean declaration is used to create an appropriate object instance and
insert it into the appropriate scope. The JSF standard provides a way for
variable lookup to be extended, and Spring provides an adapter that makes Spring
bean declarations accessable to JSF just like managed beans declared in the standard manner.

While "managed beans" declared using the standard JSF syntax can only be declared with
app, session or request scope it is possible with Spring 2.0 to declare custom scopes.
Orchestra makes "conversation scopes" available for use. When a bean is instantiated
which is declared to be part of "conversation Foo" then the conversation with that name
is looked up and the bean inserted into it. This scope is user-specific (ie is a child
of the session scope) and is created if it doesn't yet exist.

So far, the effect is just the same as using session scope for these beans.
However a conversation acts as a container for all the beans configured
with a particular conversation name. When a conversation ends, all beans
associated with that conversation can then be discarded together which is
difficult to achieve with simple session storage. A conversation can be
terminated in a number of ways:

access-scoped conversations end when a request occurs that does not access any bean in that conversation;

a JSF endConversation component is provided that can be inserted into a page;

a direct call can be made from a backing bean, eg after performing a "save" or "cancel" operation;

a conversation timeout can be configured to automatically expire conversations after a specified time limit.

And as conversations have names, multiple conversations (bean groupings) can exist concurrently.

Conversation names are declared simply by specifying attribute orchestra:conversationName on
the Spring bean definition. If no name is provided, then the bean is placed in its own
private conversation (which happens to have a name equal to the bean name).

A conversation can have a lifetime of "access" or "manual".
An access-scoped conversation is automatically ended (ie deleted) if a request is executed
which does not reference any bean in that conversation's scope. This is
very convenient when a sequence of pages all have at least one reference
to a bean of that conversation scope. If the user navigates to any other
page (via direct url entry, or clicking a link, etc) then after that new page is
rendered the old (obsolete) conversation scope is automatically discarded. Only
when a user's path through the application can reference pages that do not reference
conversation-scoped beans is the "manual" conversation necessary - and in that case, an
explicit endConversation component (or direct API call) must be used to discard beans
when no longer needed.

Orchestra also provides the concept of a "conversation context", which holds
a set of named conversations. A "separateConversationContext" JSF component
creates a new context. When this is a parent of any command component
(eg a commandLink) then a new conversation context is automatically
created when that command is executed. This allows multiple windows
to access the same site while having completely independent sets of
objects that are of "conversation scope". A hidden "id" emitted into
pages specifies what the current conversation context is, ensuring the
new windows "sticks" with its associated conversation context.

Orchestra Conversation Scope Limitations

Notes for Seam Users

The JBoss Seam library also provides a conversation scope. The purpose of this
scope is the same in Orchestra and Seam, but the details are a little different.
We aren't Seam experts, so this section is just our best attempt at describing
the fundamental differences; please contact the mailing list if you are a Seam
expert and have corrections for this section...

Seam's conversations are "request-centric", ie a request always has an associated
conversation (which may be "transient", ie of request scope!). Beans can be bound
to the current conversation via "out-jection" annotations. Therefore it makes sense
to ask "what is the current conversation for this request" from anywhere, even when
the object making the call is not "in" a conversation.

Orchestra instead is "bean-centric", ie a bean instance may have an associated
conversation, but a request does not. A request can access multiple beans, and each
of those beans can potentially be in a different conversation. A method can ask
"what conversation am I being invoked in", ie what conversation-scoped bean has
invoked the current method (and the answer may be "none"). Note that having
multiple conversations is really useful for pages where different parts of the page
(different "panels") have different lifetimes.

Seam's concept of "nested" conversations isn't generally needed in Orchestra, as
Orchestra supports multiple concurrent conversations (each with a separate "name").
In the case where a page calls itself, Orchestra does then need to "nest" data,
as the beans will try to use the same conversation. In this case, a nested
conversation-context must be used.

Seam uses an @Begin annotation on a method (or a non-standard "begin-conversation"
JSF navigation rule) to create a long-lived conversation. Orchestra does it automatically;
whenever an instance of a bean is created which is marked as being in a conversation "foo"
then conversation "foo" starts. Seam uses out-jection annotations to indicate which beans
belong in the conversation; instead with Orchestra the bean declarations themselves indicate
which conversation the bean instance is in.

Both Seam and Orchestra need a mechanism to end a conversation. For Orchestra's
"manual-lifetime" conversations, the o:endConversation tag or a call to method
Conversation.getCurrentInstance().invalidate() are pretty much equivalent to the
Seam @End annotation. For Orchestra's "access-lifetime" conversations, the conversation
is terminated automatically; we are not aware of any Seam equivalent for this.

Seam's conversation-context is almost identical to Orchestra's conversation-context.
However with Seam, pages need to explicitly include the context id into urls, by
using EL expression #{conversation.id} or the s:conversationPropagation tag where
appropriate. Orchestra automatically does this; instead, only links that should not
propagate the conversation context need to be marked (using the o:separateConversationContext
tag). This works for all JSF components as long as they call ExternalContext.encodeURL(),
which all code that generates URLs should do.

Seam's "conversation switcher" functionality is equivalent to switching between different
Orchestra "conversation contexts".

Notes for Spring WebFlow Users

The Spring WebFlow library also provides a conversation scope. The purpose of this
scope is the same in Orchestra and Seam, but the details are a little different.

WebFlow appears to have a similar approach to Seam (request-centric rather than
Orchestra's bean-centric approach). See the first couple of paragraphs above
describing the Seam/Orchestra differences for the implications of this.