Return-Path:
+ 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.
+
+ Write a single managed bean for the view. Configure this bean as belonging
+ to an unnamed access-scoped conversation and reference it as normal from
+ your presentation pages (jsp, facelets, clay, etc).
+
+ The conversation will be started when the bean is first accessed, and will
+ 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.
+
+ 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. 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.
+
+ 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".
+
+ Use one access-scoped controller for the whole conversation, plus one simple
+ request-scoped or access-scoped bean per view. For example, if you have three
+ pages then structure things as follows:
+
+
+
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:tx="http://www.springframework.org/schema/tx"
+ xmlns:aop="http://www.springframework.org/schema/aop"
+ xmlns:sa="https://spring-annotation.dev.java.net/context"
+
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
+ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
+ http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
+ >
+
+ <tx:annotation-driven transaction-manager="transactionManager" />
+ <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
+
+ <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
+ <property name="scopes">
+ <map>
+ <entry key="conversation.access">
+ <bean class="org.apache.myfaces.orchestra.conversation.spring.SpringConversationScope">
+ <property name="advices">
+ <list>
+ <ref bean="persistentContextConversationInterceptor" />
+ </list>
+ </property>
+ </bean>
+ </entry>
+ </map>
+ </property>
+ </bean>
+
+ <bean id="thedataSource" class="org.apache.commons.dbcp.BasicDataSource">
+ <property name="driverClassName" value="org.h2.Driver" />
+ <property name="url"
+ value="jdbc:h2:/DATA/dummydatabase;MODE=MYSQL" />
+
+ <property name="username" value="theusername" />
+ <property name="password" value="thepassword" />
+
+ <property name="initialSize">
+ <value>2</value>
+ </property>
+ <property name="maxActive">
+ <value>100</value>
+ </property>
+ <property name="maxIdle">
+ <value>2</value>
+ </property>
+ </bean>
+
+ <bean id="entityManagerFactory"
+ class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
+
+ <property name="dataSource" ref="thedataSource" />
+ <property name="jpaDialect">
+ <bean class="org.springframework.orm.jpa.vendor.TopLinkJpaDialect" />
+ </property>
+ <property name="jpaVendorAdapter">
+ <bean
+ class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter">
+ <property name="showSql" value="false" />
+ <property name="generateDdl" value="true" />
+ <property name="databasePlatform" value="oracle.toplink.essentials.platform.database.MySQL4Platform" />
+ </bean>
+ </property>
+
+ <property name="jpaProperties">
+
+ <props>
+ <prop key="toplink.logging.level">INFO</prop>
+ <prop key="toplink.target-database">oracle.toplink.essentials.platform.database.MySQL4Platform</prop>
+ <prop key="toplink.ddl-generation">create-tables</prop>
+ <prop key="toplink.cache.type.default">HardWeak</prop>
+ <prop key="toplink.cache.size.default">5000</prop>
+ </props>
+ </property>
+ <property name="loadTimeWeaver">
+ <bean
+ class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
+ </property>
+ </bean>
+
+ <bean id="transactionManager"
+ class="org.springframework.orm.jpa.JpaTransactionManager">
+ <property name="entityManagerFactory"
+ ref="entityManagerFactory" />
+
+ </bean>
+
+ <bean id="jpaPersistentContextFactory" class="org.apache.myfaces.orchestra.conversation.persistenceContexts.JpaPersistenceContextFactory">
+ <property name="entityManagerFactory" ref="entityManagerFactory" />
+ </bean>
+ <bean id="persistentContextConversationInterceptor" class="org.apache.myfaces.orchestra.conversation.spring.PersistenceContextConversationInterceptor">
+ <property name="persistenceContextFactory" ref="jpaPersistentContextFactory" />
+ </bean>
+
+ </beans>
+
+
+ This configuration enables load time weaving of the orm classes and thus allows
+ lazy instantiation of classes. The main issue with this configuration is, that
+ Tomcat still needs a javaagent passed down as parameter. If this is not done
+ the loadtime weaving ultimately will fail.
+ the required javaagent is:
+
+-javaagent:<path to the spring weavers>/weavers/spring-agent.jar
+
+ There are weaverless configurations as well, in spring 2.0.2 it was not yet possible to enable
+ them for jpa and Tomcat. This situation might change soon, please refer to the Spring documentation
+ for additional information on how to enable the loadtime weaving of jpa classes.
+
+
+
+
+
+
Bean name: purchaseStep1
Bean name: purchaseStep2
Bean name: purchaseStep3
+ 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. +
++ The per-view beans handle logic and state that is specific to that page. If there is state needed + for the page, then use "access" 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. +
++ 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. +
++ EL expressions in the pages can reference either the per-view bean or the common controller bean, + whichever is appropriate. +
++ There are two problems with workflows: +
+ A conversation may timeout in the middle of a conversation + (eg the user goes to lunch, then tries to continue on return), and +
++ A user may try to leap into the middle of a conversation (eg via a bookmark) +
++ The basic check for the existence of the conversation is fairly simple to do in java code: +
+public void initView() +{ + ConversationUtils.ensureConversationRedirect("purchaseController", "/purchaseStep1.jsf"); +} ++ 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. + + +
+ There is one issue: after redirect it would sometimes be nice to display an error + message indicating *why* the redirect happened, eg "Invalid Conversation State". This + is not easy on a redirect. A forward would be easier to handle, as that data could + be placed in request scope. +
++ 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. +
+ ++ The orchestra core15 module provides the @ConversationRequire annotation to make this even easier. +
+ ++ 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. +
+ +
+ Notice: There is also a JsfConversationUtils
-class which allows
+ you to invoke a navigation rule rather than encoding a url in the call to
+ method ensureConversationRedirect.
+
+ 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. +
+ + + ++ In some cases a conversation should not terminate until page N has been visited, + but in the middle a user can go off and visit a page that has no references to + any managed beans within the main conversation. When using Access scopes, Orchestra + will interpret a visit to such as page as the user "abandoning" the conversation, + so the conversation will be discarded. In this case, use a manual scoped conversation, + ie one that must be explicitly ended via either a JSF component (ox:endConversation) + or a call to the Orchestra API from an action method. Use access scopes where possible, + though - they are less work. +
+ ++ 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. +
++ 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. +
++ We recommend you read about component binding and the scoping problem. + This document is not specifically about Apache MyFaces Orchestra, but the same issues apply when dealing + with component-bindings and conversation-scoped beans. +
++ JSF components interact with backing beans. However the lifecycles of these two types of + objects are very different; beans may last just a request, or longer (eg a session bean). + A component logically may last a number of page views, but physically new instances are + created for each request, when the view is "restored". +
++ JSF is designed so that in normal cases views "pull" data from backing beans, "push" data entered by the + user into backing beans, and "invoke" event-handler methods on backing beans. In all cases, the dependency + is unidirectional: views know about beans, but beans do not know about views. And the coupling is done + only via EL strings, not hard references. This means that the lifecycles of the view components and + the backing beans they reference are decoupled, and the lifecycle mismatch is not an issue. +
++ However JSF does provide a mechanism for backing beans to access view components in the rare cases where + this is necessary: "bindings". Unfortunately the coupling here is done via direct object references, not + EL strings. And if a bean then holds on to these references then very bad things can happen if the + lifetime of the bean is longer than a request. Just one of the possible problems is the ugly + "duplicate component id" error. +
++ Note that "bindings" are not generally a good idea, and should be avoided where possible. In the + remainder of this page we assume that you have some very good reason for wanting bindings, and + show you how they can be used with non-request-scoped beans. +
++ In order to avoid the lifecycle mismatch between components and beans, any bean that stores a component + binding must be request-scoped. +
++ However what has been recommended in the "Orchestra best practices" is an access-scoped backing bean for + a view and an additional (access or manual) conversation-scoped controller for multi-page conversations. + The page controller/model now is stateful, which is exactly what we wanted, however when component bindings + are needed then they should not be stored on these beans because they are not request-scoped. +
++ Therefore the following construct is bound to fail: +
+<component binding="#{viewcontrollerbean.bindingattribute}" /> ++ due to the difference in scopes. + +
+ There are two possible solutions to this problem. +
++ Spring in 2.0 has introduced a construct called aop-proxy. This is a special tag which can be embedded + into beans which basically does nothing more than to weave a proxy object around an existing object which inherits + the scope of the referencing bean. + The inner part of the proxy or own bean then can have a scope of its own which can be smaller than the scope + of the referencing object. +
++ So how does this help? Lets look again at our example +
+<component binding="#{viewcontrollerbean.componentbindingmodel.bindingattribute}" /> ++ +
+ The accessor path has slightly changed, we have introduced a second bean; a model + bean which specifically holds our component bindings. +
++ What happens on the spring configuration side is simply the following: +
+<bean id="componentbindingmodel" scope="request" class="path.to.our.model.class"/> +<bean id="viewcontrollerbean" scope="conversation.access" ...> + <property name="componentbindingmodel" + ref="componentbindingmodel" /> +</bean> ++ +
+ The associated component binding model class is a class which only holds the components as attributes and + provides associated setter and getter methods. The viewcontrollerbean class can then invoke its + methods to get access to the bound component instances. +
++ When the viewcontrollerbean object is instantiated, Spring sees that it must inject the bean named + "componentbindingmodel" into it. However that bean is marked as an aop:proxy, so what spring injects + is just a "proxy" object, not a real componentbindingmodel bean. Whenever the viewcontrollerbean + then invokes a method on that proxy object, the proxy looks up bean "componentbindingmodel" in + the specified scope (request), then invokes that method on the object it found. This does have a + significant performance impact; each method call on the proxy performs the bean lookup again. + However it always acts on the "latest" version of that bean, which would not happen if a real + reference had been held. At the end of the request, the request-scoped componentbindingmodel object + is discarded as normal, and only the proxy "shell" remains. At the next request, a new componentbindingmodel + instance is created and added into the request scope, and methods invoked on the proxy will then + automatically work against this new object. +
++ Only a few of the methods on the non-request-scoped backing bean will need access to the bound + components. Therefore, these methods can be moved into a request-scoped bean. Add the component + binding methods to this request-scoped bean too, and inject a reference to the "real" backing + bean into it. Alter the view so that EL expressions that need those component bindings to + be evaluated point to the request-scoped bean. +
++ Another way of thinking about this is that the "real" backing bean for the page is implemented as + request-scoped, and any state it needs to retain is pushed into a "helper" object that is of + conversation scope. The request-scoped bean has access to the components without problems. +
++ While either of these solutions means yet another backing-bean class for the view, there is no + "spring magic" required. +
++ 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. +
++ 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.
+ 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. +
++ Conversation-scoped beans are always stored (indirectly) in the user's http session. + This means that Orchestra requires server-side storage. +
++ 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". +
++ 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. +
+
+ Depending on your environment and used JPA-implementation you might experience the problem
+ that there are database connections which have not been passed back to the connection pool.
+ This can happen if you use entities with e.g. OneToMany mappings and lazy init AND accessing the
+ OneToMany collection from your JSF view the first time.
+ The EntityManager has to re-establish a connection to your database, but now that this happens
+ outside of your conversation scoped bean, no one ends the transaction and so no one puts the
+ connection back to the pool.
+ The problem is that with a growing number of users and a growing number of conversations
+ (especially not ended conversations - waiting for their timeout) your pool might exhaust.
+ As said before, this might not be the case with all JPA-implementations. But we had at least
+ one environment where this happened, so here the work-around.
+
+ To activate the workaround simply put the following code in your spring config and adjust the + names as required. + +
+<bean id="managedDataSource" class="org.apache.myfaces.orchestra.connectionManager.ConnectionManagerDataSource"> + <property name="dataSource" ref="dataSource"/> +</bean> ++ + The basic principle is to put a virtual DataSource between the JPA-implementation and + the read DataSource (which should be a connection pool for performance reasons).
the property dataSource
to point to the original datasource and
the JPA configuration to use our managedDataSource
(adjust the name if required)
+ Use the <aop:scoped-proxy>
configuratino with in the
+ persistentContextConversationInterceptor
definition.
+ You'll end up with something like:
+
+<bean id="persistentContextConversationInterceptor" class="org.apache.myfaces.orchestra.conversation.spring.PersistenceContextConversationInterceptor"> + <aop:scoped-proxy/> + <property name="persistenceContextFactory" ref="persistentContextFactory"/> +</bean> ++ +
+ The reason is, that, during startup of the spring system when the custom scope will
+ be initialized, the system might already require a datasource, but at this time
+ the PropertyPlaceholderConfigurer
didn't run already.
+ Using the <aop:scoped-proxy>
configuration allows the initialization
+ process to end cleanly.
+
+ If your ViewController event methods are called twice you probably configured the
+ ViewControllerPhaseListener in your faces-config.xml which is not required.
+ Orchestra already does this for you.
+
No!
+ Each conversation has its own persistence context, but an entity can only be managed
+ by the context it was loaded by.
+
+ Reading an entity may trigger lazy loads. This will always be performed using the
+ persistence context that loaded the entity, ie this works regardless of who calls
+ the method on the entity.
+
+ However performing operations like merge, delete, etc. require the object to be
+ passed to a persistence context; if it is passed to the wrong one then unexpected
+ behaviour will occur. Things like using the object in a query being executed in
+ a persistence context that is not the one associated with that entity may also
+ fail.
+
+ Rather than passing entity references between conversations, just pass the key of the
+ object and reload it if access is required from a different conversation.
+
+ This is not an Orchestra limitation, but more a technical limitation of how ORMs work today.
+
property | + +member | + +getter | + +setter | +
---|---|---|---|
userId | + +private String userId | + +getUserId | + +setUserId | +
userName | + +private String _userName | + +getUserName | + +setUserName | +
displayed | + +private boolean displayed | + +isDisplayed | + +setDisplayed | +
+ 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. +
++ 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). +
++ The proxy object returned by orchestra during a bean lookup does not directly hold + a reference to the underlying bean. Instead it just keeps: +
+ Invoking any method on the proxy object causes it to: +
+ 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. +
++ 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. +
++ 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. +
++ 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. +
++ 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. +
++ 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. +
++ 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. +
+