UI-Component Sets
Project Documentation

Overview

To understand what features Orchestra can provide, and what its limitations are, it can be helpful to understand how it works. This is entirely optional, and you can skip this section if you wish.

Please note that the information in this section describes how the current implementation works. This is subject to change in future releases.

Proxies

Orchestra uses object "proxies" that intercept calls to real beans in order to track data flows and to redirect them as appropriate.

When a bean is configured to live in an Orchestra scope, any time an instance of that bean is created then what is returned is not a reference to the actual type, but instead a proxy object. The proxy object is of the same type (ie "x instanceof TheExpectedType" will return true). If you use getClass() on the proxy object, it may show that the proxy is a subclass of the expected type (depending on how the proxying is implemented). However in most cases this doesn't matter; the proxy can be treated exactly as if it was of the expected type, and of course provides all the same methods that the expected type supports.

Of course this relies upon instances being created in a way that Orchestra can intercept in order to create the proxy object. The most common usage is expected to be JSF with Spring; in this setup a custom JSF VariableResolver is automatically registered so that EL expressions such as "#{myBean}" cause Spring to handle the lookup, and Orchestra's Spring scope-handler will then automatically proxy the bean. However other presentation-tier approaches (eg plain jsp or servlets) and other bean-management implementations (such as Google Guice) should be usable with Orchestra as long as there is a mechanism for the appropriate proxies to be created when needed.

The proxy has two distinct functions: to direct calls to an appropriate "real" underlying bean (similar to Spring's aop:scoped-proxy), and to run an arbitrary set of "advices" around each method call on the proxy. In particular, an advice is used to implement Orchestra's persistence support (see later).

Indirection

The proxy object returned by orchestra during a bean lookup does not directly hold a reference to the underlying bean. Instead it just keeps:

  • a reference to a Scope object
  • a conversation-name string
  • a bean-name string

Invoking any method on the proxy object causes it to:

  • fetch the ConversationManager object for the current user
  • fetch the current ConversationContext for the user's current window
  • fetch the correct Conversation instance (using the conversation-name)
  • fetch the correct target object from the Conversation (using bean-name), or (if the instance does not yet exist) create an instance and store it in the conversation.
Then the originally invoked method is forwarded to the target object that was found.

Any call on a method of the proxy will therefore be forwarded to an instance that is stored in a specific Conversation object. If the Conversation object is deleted, then invoking a method on the proxy will just create a new Conversation instance and a new instance of the target bean. Because of this, proxy objects can be retained for any length of time; they never point to "stale" objects. This in turn allows Orchestra to discard conversation objects at appropriate times without breaking code holding references to those objects.

There is of course a performance penalty for this; invoking a method on a conversation-scoped object (via its proxy) require a couple of hashmap lookups to locate the target object, plus whatever internal overhead the proxying library (currently CGLIB) adds.

Note that in Orchestra Core 1.0 a spring aop:scoped-proxy tag was needed on bean definitions in order to enable the above functionality. Orchestra does function without the indirection feature, but code must then be careful not to store any such references for later use; instead beans can only be referenced via EL expressions. It was strongly recommended in Orchestra 1.0 that aop:scoped-proxy be added to each conversation-scoped bean definition and it is now automatic in Orchestra Core 1.1.

Storing Conversations

Conversations are stored within a ConversationContext which is stored within a ConversationManager which is in turn stored within an HttpSession.

A conversation and its associated data therefore lasts as long as the user's session object, unless explicitly deleted by Orchestra.

Access Scope

When a proxy is invoked, and retrieves a target bean from a specific conversation it also marks the conversation as "accessed". This allows Orchestra to determine whether a conversation is currently "in use", or whether the user has navigated off to some other part of the webapp. When the conversation is marked as "access" scope, and is not in use then it is simple to detect this and discard the conversation (together with all the bean instances that are stored in it).

Because proxies simply trigger the creation of new instances (and a conversation to hold it) when needed, a user returning to an "access scope" page simply causes a "fresh" version of the page state to be created.

This approach is quite different from some other libraries that provide conversation scope. In particular, it does not require any elaborate flow-control state charts to control navigation. With Orchestra the detection of beans that are no longer in use is completely automatic.

Manual Scope

With manual scope, Conversation instances are only deleted when application code makes an explicit call to the Orchestra API, or when a JSF tag in a page makes that call. The application therefore itself determines when a set of beans (ie a conversation) is no longer needed. The implementation of this is obvious: a ConversationContext object holds a map of conversations by name, and this just removes the appropriate (name, Conversation) entry from that map. All the beans in the conversation then become elegible for garbage-collection. Note that Orchestra provides an option for beans in a conversation to be notified when the conversation is terminated, which provides a kind of "destructor" opportunity.

Multiple beans in a single Scope

Each conversation has a name. By default, a conversation-scoped bean will be placed in a conversation whose name is the bean-name used to retrieve that bean from the dependency-injection system (Spring or other). Therefore by default each conversation has only one bean in it.

Configuring a group of beans to belong to the same conversation is quite useful with both Access and Manual scopes. To do this, simply ensure that the configuration on the bean declaration specifies a conversation name. The Conversation object has a Map of the objects in it, and so therefore can hold any number of different beans, keyed by their bean-name.

Multiple Window Support

An http session is typically maintained via a "cookie" that identifies the session to be used for each request. A web-browser can open multiple windows or tabs which share the same cookies; in this situation requests from different windows will all use the same http-session on the server, and therefore access exactly the same instances of session-scoped objects.

Unfortunately reusing session-scoped objects for completely unrelated requests coming from different windows or tabs of a user's browser can cause nasty problems.

The Java Servlet standard does not support more than one session per user. However Orchestra effectively provides this by keeping conversation objects as children of a ConversationContext object. All that is needed is for each window to specify a different ConversationContext object to use. Further documentation on this feature can be found elsewhere on the Orchestra site.

Persistence

Each conversation can optionally have a PersistenceContext associated with it. When this is configured, then after a proxy has been invoked and has determined which conversation the target bean is in, it configures the PersistenceContext associated with that conversation as "the global persistence context". Any persistence operations performed by the target bean (or the ordinary instances it calls) therefore run in the context of the persistence context for that conversation. When the method on the target bean returns, the proxy restores the previous persistence context.

Conversation instances are stored (indirectly) in the user session, so the persistence context objects last across multiple requests, and are only discarded when the associated conversation object is discarded. This allows "long running" persistence operations.

As noted elsewhere, this is useful only in applications where the database access and the web presentation logic exist in the same physical tier. This is common for small to medium applications, but the largest and most security-sensitive applications typically keep database access logic in a separate tier and therefore this Orchestra feature cannot be applied. However there are many single-tier applications where this functionality can save a lot of developer time and effort.