Spring into Seam, Part 2: When stateless and stateful collide

Add stateful behavior to your Spring beans

1 2 3 4 5 Page 3
Page 3 of 5

Finding the right balance with injection

Seam-scoped Spring beans can be injected into other Spring beans using the familiar <ref bean="beanId"> (or equivalent) syntax. In this case, Spring is transparently reaching into the Seam container to grab the instance of the bean out of a Seam scope. The transparency is achieved through the custom scoping mechanism, which is Spring's version of a variable resolver. This scenario is the opposite of what you learned about in the first article, where Seam was transparently pulling beans from the Spring container. The difference is just a matter of which container the instance is stored in, as dictated by the scope defined on the Spring bean definition. Otherwise, a Spring-Seam hybrid can mask itself as a native component in either container. That means you can also inject any Seam component (including another Spring-Seam hybrid component) into a property of a Spring-Seam hybrid component using the @In annotation.

That brings us to an important point about injection that I would like to draw your attention to. While a Spring-Seam hybrid can leverage the injection mechanism from either container, you can quickly get into trouble if you don't understand how and when the injections are applied in each case. For instance, let's assume you are injecting the search criteria into the tournamentManager bean. You might decide to use a bean reference when initializing the property in the bean definition, as in Listing 6.

Listing 6. Injecting a Seam-scoped bean using a bean reference

<bean id="tournamentManager"
  class="org.open18.partner.business.impl.TournamentManagerImpl"
  scope="seam.APPLICATION">
  <property name="tournamentSearchCriteria" ref="tournamentSearchCriteria"/>
</bean>

You could also accomplish the injection by using the @In annotation, as in Listing 7.

Listing 7. Injecting a Seam-scoped bean using injection

public class TournamentManagerImpl implements TournamentManager {
    @In
    private TournamentSearchCriteria tournamentSearchCriteria;
    ...
}

The manner in which these injections are applied by the two containers is very different. And neither one is appropriate for injecting stateful components into an application-scoped singleton! Spring injects dependencies once at creation time, whereas Seam performs injection dynamically, before every method invocation. This difference in design philosophy can fall heavily on your shoulders as a developer, leading to a paradox known as scope impedance. If you declare your injections carelessly, it can cause serious problems. In the next section, you'll learn how to work around this potential pitfall.

Injection and scope impedance

Scope impedance is the situation where a bean outlives its intended lifetime as a result of attaching itself to a longer-lived component. Having the Seam scopes at hand when authoring your Spring configuration, you may be tempted to define a bean in a narrow scope and inject it into a Spring singleton, which is scoped to the lifetime of the container (the application context), using <ref bean="beanId">. Without any other provisions, this leads to scope impedance. Let's explore this problem.

For the purpose of this next example scenario, we will assume the use of two beans. The wider-scoped bean is an application-scoped singleton, such as a service layer object (e.g., tournamentManager). The narrow-scoped bean is stored in the request scope (e.g., selectedTournament). The request-scoped bean is a dependency of the singleton. When the singleton is instantiated, the request-scoped bean, populated by the incoming request, is injected into one of the singleton's properties. However, once the request ends, the singleton will still hold a reference to the request-scoped bean. Now the request-scope bean has outlived its designed lifetime. Not only that, it is being shared amongst all users of the application because it is attached directly to an application-scoped singleton!

So why isn't Seam managing the request-scoped component so that it is cleaned up after the request ends as it normally would be? The reason this happens is because the requested-scope bean instance is assigned directly to a property of the singleton. When the request scope ends, Seam merely removes bean instances from the request context. The property values of the singleton objects are not affected. Since the singleton is maintaining a reference to the request-scoped bean instance, the object is never garbage collected. On the next request, the singleton retains its reference to the stale instance. As a result, the singleton may make inappropriate decisions based on the state of the stale instance, which is especially problematic if the next request does not come from the same user! This situation not only causes unexpected behavior, but can also compromise security.

1 2 3 4 5 Page 3
Page 3 of 5