2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

Shale Spring Integration

Introduction

The Spring Framework is a popular application framework for Enterprise Java applications, featuring a powerful Dependency Injection mechanism (also known as Inversion of Control) for creating and configuring "plain old Java objects" (POJOs) on the fly. Since verson 1.1.5, Spring has included an expression evaluation mechanism (see below) for integration with JavaServer Faces. Shale adds the ability to programmatically access the Spring WebApplicationContext for the current application.

Services Provided

Spring provides a custom JavaServer Faces VariableResolver implementation that extends the standard JavaServer Faces managed beans mechanism. When asked to resolve a variable name, the following algorithm is performed:

  1. Does a bean with the specified name already exist in some scope (request, session, application)? If so, return it.
  2. Is there a standard JavaServer Faces managed bean definition for this variable name? If so, invoke it in the usual way, and return the bean that was created.
  3. Is there configuration information for this variable name in the Spring WebApplicationContext for this application? If so, use it to create and configure an instance, and return that instance to the caller.
  4. If there is no managed bean or Spring definition for this variable name, return null instead.
As a result of this algorithm, you can transparently use either JavaServer Faces or Spring facilities to create beans on demand. Note, however, that the Spring integration capabilities do not include the ability to place a Spring-created bean into a specified scope. Therefore, it works best for application singletons, or for beans where you wish to create a new instance on every request.

Separately, Shale also provides a custom VariableResolver that exposes Spring's WebApplicationContext instance for the current application, using variable name webApplicationContext to look it up. This is useful when an event handler wishes to explicitly invoke the bean factory to create beans on demand, such as a bean that encapsulates the business logic to be performed when a submit button is pressed.

Using Shale-Spring Integration

In order to utilize the Spring integration, you will need to include Spring in your web application. You can either include the all-in-one version (spring.jar) or the necessary individual components (spring-core.jar, spring-context.jar, and spring-web.jar). In order to utilize the Shale feature that exposes the webApplicationContext instance, you will need to include shale-spring.jar.

Here are some sample use cases for leveraging the integration capabilities:

(1) Bind a JSF Component To An Applicaton Scope Bean

Imagine you have an application scope bean that contains domains (the lists of selection items used in drop down components) for your application. You might bind a component to a property of this bean as follows:

  <h:selectOneMenu id="category" value="..." ...>
    <f:selectItems value="#{domains.categories}"/>
  </h:selectOneMenu>

where the underlying object referenced by the logical name domains is expected to have a getCategories() method with a return type of SelectItem[]. The underlying bean class might be defined as a managed bean in /WEB-INF/faces-context.xml:

  <managed-bean>
    <managed-bean-name>domains</managed-bean-name>
    <managed-bean-class>
      com.mycompany.mypackage.MyDomainsImpl
    </managed-bean-class>
    <managed-bean-scope>application</managed-bean-scope>
    ... configuration of managed properties ...
  </managed-bean>

or as a Spring bean in /WEB-INF/applicationContext.xml:

  <bean id="domains"
        class="com.mycompany.mypackage.MyDomainsImpl"
        singleton="true">
    ... configuration of managed properties ...
  </bean>

The binding expression will work with either definition of the bean, transparently.

(2) Access Encapsulated Business Logic Indirectly

Assume you have business logic to manage updating a customer database in a bean with logical name CustomerDAO. In the event handler for a submit button (here, assuming that your backing bean extends AbstractViewController or AbstractFacesBean), you can acquire a reference to the appropriate instance of this business logic bean by using a value binding expression programmatically:

  CustomerDAO dao = (CustomerDAO) getValue("#{CustomerDAO}");

Again, the logic utilizing this expression is agnostic to whether the bean is actually created by JSF's managed bean facility, or by Spring's bean factory.

(3) Access Spring WebApplicationContext Directly

In some circumstances, applications will want to explicitly interact with Spring's bean factory (in a web application, this is typically an instance of WebApplicationContext. In order to retrieve the application wide instance of this class for the current web application, you may evaluate a value binding expression with a particular reserved name:

  WebApplicationContext wac = (WebApplicationContext)
    getValue("#{webApplicationContext}");

Once you have this reference, you can explicitly invoke its methods to create or manage bean instances. Interacting with it, of course, is using Spring facilities directly, so you are no longer able to transparently use managed beans. However, you do have direct access to the extra facilities provided by Spring.