RSF and Spring Contexts

Information

This explains how RSF and Spring contexts work to form an RSF application. The basics of spring and various scopes within an app are covered.

Web application scopes

There are basically 3 scopes that we are concerned with in a web app.

  1. Application Scope
    Beans/Objects in this scope are shared between everything in the system (i.e. there is 1 instance of items in this scope for your entire web app). Items here will tend to survive for as long as the web app is running (in this case, for as long as the servlet container is running). Items here are accessible to anything in the same Spring applicationContext (in the case of Sakai, this would mean anything in the Component space). Items often have an unlimited lifetime. You should not have any modifiable data held within beans in this scope, this is very often a design error.
    Items like this are typically used for managing access to resources and performing functions which are shared through the web app (e.g. logic and dao beans).
  2. Session Scope
    Note: Note that in RSF, "session-scoped" beans are really just another variety of request beans. (see explanation in section below)
    Session typically refers to a user session (time between login and logout) but it could be a tool session, user/tool session, or something else entirely. Beans/Objects in this scope are unique for each session. RSF "session beans", being a kind of request beans, will be created in the request in which they are first accessed, and will persist in the session until the session ends, or until their scope is manually destroyed, whichever happens soonest. As well as being a "cheap" kind of persistence, the primary value of session beans lies in their being a kind of ''authenticated storage''. These items are only accessible within the particular session, and in general the infrastructure is set up around session state to make it (very) hard in general for users to get access to each others session state. Items have a limited lifetime but are long lived.
    Items like this are often used to maintain state in the webapp for a workflow or for the entire session. These allow data to be stored without having to pass it between every page but should be used only when necessary to avoid breaking the standard statelessness of the web applications.
  3. Request Scope
    Request typically refers to a single page or http request cycle. Bean/Objects in this scope are unique for each request and will be created at the beginning of the request and destroyed at the end. The items are accessible for a very short time and only within this request. Items have a very limited lifespan which is typically fractions of a second. RSF encourages you to make as much use as possible of the plain request scope as opposed to the session scope, which usually results in more usable and more efficient designs. Many situations which in other frameworks require the use of session beans are better handled in RSF by use of URL state (ViewParameters) and the request scope.
    Items like this are often used to process a request in some way. This includes collecting submission data and processing navigation requests.

    This image shows the state of memory in a container which is currently serving 3 web requests (X, Y and Z), on behalf of two different users/tools with sessions A and B. Note that in general access by multiple requests to the same session (X, Y => A in this case) can cause problems in apps that are not set up to deal with this, and is best prevented by using the exclusive="true" annotation (see below).

RSF and Spring

RSF uses Spring to control the lifecycle of various beans through a webapp created using it.

  • Note: You can inject any bean into any other bean as long as the scope of the injected bean is equal to or greater than the scope of the receiving bean.
    Consider that a bean at application scope can be identified uniquely to a bean in session or request scope because there is only one in the scope but going the other direction there could be multiple session and requests so those beans cannot be identified uniquely. Even if they could you would not want to try to use them because the lifetimes are radically different.
    • For example, these are valid: (-> = injected into)
      • appLogicBean -> sessionBean1-A
      • sessionBean1-A -> requestBean5-X
      • requestBean5-X -> requestBean6-X
      • appDaoBean -> appLogicBean -> sessionBean1-B -> requestBean6-Z
    • These injections would be invalid:
      • sessionBean1-A -> appLogicBean
      • requestBean5-X -> sessionBean1-A
      • requestBean5-Y -> appLogicBean
      • sessionBean1-A -> requestBean6-Z (scopes are different)
      • sessionBean2-B -> requestBean5-Y (scopes are different)
      • requestBean5-X -> requestBean5-Y (scopes are different)

  1. ApplicationContext
    RSF defines RSF related application context (scoped) beans through the applicationContext.xml file in the tool/src/webapp/WEB-INF directory. Most of these beans are used to configure and control the RSF web app. If you are using Sakai you will also likely have a components.xml file which is used to define things like logic and dao beans.
    Sample RSF applicationContext.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
    	"http://www.springframework.org/dtd/spring-beans.dtd">
    
    <beans>
    
    	<!-- For security purposes, only beans listed in the comma separated value list
    			may be the target of EL operations coming in over the request -->
    	<bean parent="requestAddressibleParent">
    		<property name="value" value="itemsBean"/>
    	</bean>
    
    	<!-- Control the location of the html templates (default: content/templates/) -->
    	<bean id="templateResolverStrategy" parent="CRITemplateResolverStrategy">
    		<property name="baseDirectory" value="templates/" />
    	</bean>
    
    </beans>
    
  2. RequestContext
    RSF defines session and request scoped beans in the requestContext.xml file in the tool/src/webapp/WEB-INF directory. The values stored in session and request beans should be set via RSF EL only. Do not set these values by accessing the bean directly. Reading values from the session scoped beans can happen at any time. Reading values from the request scoped beans can only happen within the request cycle (probably in the producer).
    Note: You should strive to use request scoped beans instead of session scoped beans whenever possible in RSF.
    1. Request
      This is the type of backing bean you should use in almost every circumstance. You should not use a session bean unless you have a need to keep the bean alive beyond the request cycle.

      All request scoped beans and producers are defined in the requestContext.xml file. As a rule of thumb, request scoped backing beans should not be anonymous (i.e. have an id set) and producers should be anonymous (i.e. have no id set). If the bean is referenced somewhere else then it needs an id but since almost all producers are not referenced there is no reason to do the extra work.
      Sample RSF requestContext.xml
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
      	"http://www.springframework.org/dtd/spring-beans.dtd">
      
      <beans>
      
      	<!-- list the backing beans here -->
      	<bean id="itemsBean"
      			class="org.sakaiproject.rsfcrud.tool.ItemsBean"
      			init-method="init">
      		<property name="logic"
      			ref="org.sakaiproject.rsfcrud.logic.RsfCrudLogic" />
      	</bean>
      
      	<!-- list the producer beans here -->
      	<bean class="org.sakaiproject.rsfcrud.tool.producers.ItemsProducer">
      		<property name="itemsBean" ref="itemsBean" />
      		<property name="logic"
      			ref="org.sakaiproject.rsfcrud.logic.RsfCrudLogic" />
      	</bean>
      
      </beans>
      
      • Note: Examples of injecting beans from a higher scope:
        (Assume that itemsBean is session scope)
        • RsfCrudLogic (application scope) is injected into ItemsBean (session scope)
        • RsfCrudLogic (application scope) is injected into ItemsProducer (request scope)
        • ItemsBean (session scope) is injected into ItemsProducer (request scope)
    2. Session
      RSF session beans are actually normal request beans which are not destroyed at the end of the regular request cycle. These beans are still initiated at the beginning of a request cycle like a normal request bean. They should be manually destroyed by the developer when they are no longer needed, but will be destroyed at the end of the session automatically.
      Warning: Do not use session beans unless you have a good reason to. You should attempt to use request scoped beans whenever possible.

      Session scoped beans are defined in the requestContext.xml just like request context beans. The only difference is that the session scoped bean ids are listed in a bean in the applicationContext.xml as shown.
      Sample bean definition from requestContext.xml
      <bean id="itemsBean"
      		class="org.sakaiproject.rsfcrud.tool.ItemsBean"
      		init-method="init">
      	<property name="logic"
      		ref="org.sakaiproject.rsfcrud.logic.RsfCrudLogic" />
      </bean>
      
      Each session scoped bean must be listed in a special bean in applicationContext.xml. The list of bean ids is just a comma delimited string of the ids defined in the requestContext.xml file. This example shows the beanScope defining one session bean with the id of itemsBean. The itemsBean bean must be defined in the requestContext.xml.
      Sample session defining bean from RSF applicationContext.xml
      <bean id="myBeanScope" parent="beanScopeParent">
      	<property name="copyPreservingBeans" value="itemsBean" />
      	<property name="exclusive" value="true" />
      </bean>
      
      In order to destroy a scope from the session (thus deallocating all the beans which it contains) you may inject the bean named after its scope (in the case above, "myBeanScope") under the interface type "BeanDestroyer". Calling the destroy() method on this object will cause all the beans it contains to be deallocated from the session. Note that this will have no effect until the end of the current request!
      For example, the producer above could receive and trigger the destroyer for this scope by adding to its definition like this:
      Extended producer definition showing injection of scope destroyer
      <bean class="org.sakaiproject.rsfcrud.tool.producers.ItemsProducer">
          <property name="itemsBean" ref="itemsBean" />
          <property name="logic" ref="org.sakaiproject.rsfcrud.logic.RsfCrudLogic" />
          <property name="MyBeanScopeDestroyer" ref="myBeanScope"/>
      </bean>
      
      The producer definition would then include a setter as the following:
      Producer snippet showing injection of scope destroyer
      public class ItemsProducer implements ViewProducer {
        private BeanDestroyer destroyer;
        public void setMyBeanScopeDestroyer(BeanDestroyer destroyer) {
          this.destroyer = destroyer;
        }
      
        public void fillComponents(UIContainer tofill, ViewParameters parameters, ComponentChecker checker) {
          // more code here
          if (myScopeEnded) {
            destroyer.destroy();
          }
          // more code here
        }
      }