Chapter 6. Absolute inversion of control

This chapter covers

  • Wiring components together dynamically
  • Applying method interceptors
  • Raising and observing component events
  • Resolving context variables on demand

Inversion of control (IoC) is a pattern in aspect-oriented programming (AOP) that espouses loose coupling, allowing the application to focus on consuming services rather than locating them. Seam embraces the use of IoC not only to “wire” components together but also to produce context variables and to enable components to communicate with one another through event notifications. Often, when people talk about IoC, they’re really talking about dependency injection (DI), one use of IoC and the primary focus of this chapter.

Dependency injection is a key concept in POJO-based development that keeps components loosely coupled. In the previous chapter, you learned how static DI can be used to establish references from a component instance to its dependent objects during instantiation. In this chapter, you’ll learn about another assembly mechanism in Seam that links a component instance to its dependencies when it’s invoked, a device known as injection. To complement injection, outjection facilitates exporting state from a component instance after the component is invoked, effectively producing context variables that can be used elsewhere in the application.

This chapter begins by introducing the four steps of bijection—injection, method invocation, outjection, and disinjection. We then explore several derivatives of bijection. One such variant manages “clickable lists” in a way that’s completely transparent to the business component. The key to bijection is that it’s dynamic, meaning it happens every time the instance is invoked, not just at creation time. Events, which you’ll learn about next, go a step further toward decoupling components by allowing execution logic to jump from notifier to observer without an explicit reference between the participating components. Events let you add features without disrupting well-tested code or relieve a single method from trying to handle too many concerns. Going beyond the built-in behavior that Seam weaves into components, you’ll learn how to create custom interceptors to handle your own cross-cutting logic. Shifting back to the subject of context variables, you’ll discover how to use factory and manager components to produce variables requiring more sophisticated instantiation, typically with the help of bijection. Let’s begin by exploring what makes bijection so unique.

6.1. Bijection: dependency injection evolved

Dependency injection is about as mainstream as MySpace or the iPod. Although it may not be a hot topic at family gatherings (at least not mine), it’s the cornerstone of POJO-based development. In DI, the container “injects” dependent objects into the corresponding properties of a target object after instantiating it, establishing references from the object to its dependencies (a process commonly referred to as bean wiring). This strategy eliminates lookup logic within the object that might otherwise make it reliant on a particular environment. The object is said to be loosely coupled from the rest of the application. Unfortunately, it’s not loose enough.

For as much as DI has been studied, discussed, and presented, it hasn’t changed much since its emergence and suffers from several limitations. The first is that the injections are static, meaning they’re applied only once, immediately following instantiation of the object. As long as the object exists, it’s stuck with these initial references, failing to reflect the changing state of the application over time. Another limitation of DI is that it’s focused only on the assembly of objects. It would be just as useful for an object to be able to contribute back to the container by transferring its state to one or more scoped variables.

These limitations highlight a general unawareness of context in the design. The managed objects need to be made more aware of the application’s state and become active participants.

6.1.1. Introducing bijection

Seam has responded to this call for change by introducing bijection. Under bijection, the wiring of dependencies is done continuously throughout the lifetime of a component instance, not just when the instance is created. In addition to injecting values, bijection supports outjecting values. Outject? Pencil it into your dictionary—it’s a new term. Outjecting is the act of promoting the value of a component property to a context variable where it can be picked up by another component or referenced in a JSF view, a page descriptor, or even a jBPM business process definition.

The combination of injection and outjection is known as bijection. Bijection is managed by a method interceptor. Injection occurs prior to the method being invoked, and outjection occurs when the invocation is complete. You can think of the context variables participating in this interaction as flowing through the component as it’s invoked. Injection takes context variables from the Seam container and assigns them to properties on the target instance, and outjection promotes the value of its properties to context variables. Figure 6.1 provides a high-level conceptual diagram of this mechanism.

Figure 6.1. Bijection wraps a method call, performing injection before the call and outjection afterward.

Before you put your dictionary away, I’ll mention that Seam also disinjects values from a component. In this step, any property that received an injection is assigned a null value. Disinjection is Seam’s way of tying up loose ends. Seam can’t keep the references up to date while the component instance is idle, so the values are cleared to avoid state from lingering. The injections are restored the next time the component is invoked.

 

Note

Bijection modifies the state of the component instance. Therefore, calls to intercepted methods must be synchronized. Fortunately, Seam efficiently synchronizes all components except for those in application scope.

 

Although bijection may share some similarities with traditional DI, it’s genuinely a new approach that makes context a primary concern. As you read through this section, you’ll explore, in detail, how the bijection process works, how it affects the relationship between components, and how to put it to use.

6.1.2. Bijection on the golf course

Before diving into the technical aspects of bijection, I want to start off with an analogy. When you’re playing in a golf tournament, you want all of your focus on your game. To help avoid distractions, you are provided a caddy, who acts as your assistant. The role that the caddy plays between each stroke of the ball parallels that of bijection. In this analogy, you are the component, the golf club is the dependent object, and the stroke is the method call.

As you approach your golfball, in whatever context it may be lying—the green, the fairway, a sand trap, on top of a warehouse, or the woods—you aren’t holding the golf club that you need to take your stroke. The golf club is your dependency. To satisfy this dependency, your caddy injects the golf club that’s appropriate for the context—a wood, iron, wedge, or putter—into your hands. You are now ready to swing. Striking the ball is the action, or, in the case of bijection, the method invocation. After taking your stroke, the ball is outjected and lands in another context on the course—hopefully avoiding bodies of water and sand traps. Outjection occurs as the result of a method call. Once the shot is taken, the caddy disinjects the club, reclaiming the dependency that was previously handed to you, and stores it in your bag for later use. You walk away from the original spot of the ball the way you arrived, empty handed. (You may still hold state, like your scorecard, but not the dependency.)

The caddy lets you concentrate on your golf game, rather than on the routine of carrying the clubs, cleaning them, and remembering not to leave one behind. It’s an inversion of control. In the same way, bijection allows you to concentrate on the business logic rather than rounding up dependent objects needed to perform the logic and distributing results afterward. As with DI, bijection makes components easy to test in isolation or reuse since there’s no tight coupling to a container-specific lookup mechanism.

With a general understanding of how bijection works, let’s dig into the technical details of how you can add this capability to your components.

6.1.3. Activating bijection

When Seam instantiates a component, it registers a handful of method interceptors on the instance, an AOP technique for applying cross-cutting concerns. One of those interceptors manages bijection. Like all method interceptors, the bijection interceptor is triggered each time a method is called on the instance, referred to here as the target method. The bijection interceptor wraps the call to the target method, performing injections before the target method proceeds and then outjections, followed by disinjections, after the target method executes—all of which takes place before the method returns to the caller.

The properties that participate in bijection are designated using annotations. The most common bijection annotations are @In and @Out, which define injection and outjection points, respectively. Derivatives of these annotations also exist, but they’re processed in fundamentally the same way. For now, we’re going to focus on @In and @Out. Here’s the first version of the ProfileAction component, which uses both @In and @Out. It selects a Golfer instance by ID from the injected EntityManager and then promotes the instance to a context variable that can be accessed from the view. Assume for now that the golfer ID is passed to the view() method defined below by a parameterized expression bound to a UI command button.

  @Name("profileAction")
  public class ProfileAction {
      @In protected EntityManager entityManager;
      @Out protected Golfer selectedGolfer;

      public String view(Long golferId) {
          selectedGolfer = entityManager.find(Golfer.class, golferId);
          return "/profile.xhtml";
      }
  }

While this component may look straightforward at first glance, upon further examination you may question how the @In and @Out properties work, knowing that annotations are just metadata. What happens is that Seam scans for @In and @Out annotations when the component is registered and caches the metadata. The bijection interceptor then interprets this metadata when a method is called on an instance and applies bijection to its properties.

When the bijection interceptor traps a call to a component method, it first iterates over the bean properties on the component marked with an @In annotation and helps those properties find the values for which they are searching. If all the required @In annotations are satisfied, the method call is allowed to proceed. From within the method, the property values that were initialized via injection can be accessed as if they had been there all along.

If the method throws an exception, bijection is interrupted and control is turned over to the Seam exception handler. If the method completes without exception, the bijection interceptor postprocesses the method call. This time, it iterates over the bean properties marked with an @Out annotation and promotes the value of these properties to context variables in the Seam container. Finally, the properties that received injections are cleared, wiping the slate clean for the next invocation. The bijection process just described is illustrated in the sequence diagram in figure 6.2.

Figure 6.2. The bijection interceptor traps a method call on a component instance and performs the four steps of bijection—injection, method invocation, outjection, and disinjection.

That should give you enough technical details to convince your boss that you know what bijection is, but you may need to see some examples to become comfortable using it. You’ll also need to figure out how Seam locates a value to inject and which context variable is used when a property is outjected. Without these key details, bijection remains a bit mystical.

Most of the time, you’ll be using the injection piece of bijection. Let’s put a spin on a well-known phrase by saying “no component is an island” and explore how injection is used to “wire” components together dynamically.

6.2. Dynamic dependency @In-jection

Implementing business logic typically involves delegating work to other components. A familiar delegate that shows up in nearly every database-oriented application is the persistence manager (e.g., JPA EntityManager or Hibernate Session), which is used to persist entities or read their state from the database. In the previous chapter, you used component configuration to perform this injection—a form of static DI. That’s fine if you’re injecting a stateful component into a short-lived component or injecting a stateless component. However, as soon as you start using stateful components that interact with other stateful components, you need a mechanism that keeps the references up to date. Rather than trying to distinguish between the two cases, the recommended way of hooking components together in a Seam application is to use bijection. That way, you can always be sure the component will adapt to changes in the application’s state.

6.2.1. Declaring an injection point

Annotations come into play when configuring bijection as they did when defining Seam components. The @In annotation, summarized in table 6.1, is placed above a bean property of a component—either a field or a JavaBean-style property “setter” method. Seam uses reflection to assign a value to properties marked with @In during the first phase of bijection.

Table 6.1. The @In annotation

Name:

In

Purpose:

Indicates a dependency on a context variable, which should be satisfied via injection

Target:

METHOD (setter), FIELD

Attribute

Type

Function

value String (EL) The context variable name or value expression used to locate a value to inject. If this attribute is not provided, the name of the property is used as the context variable name. Default: the property name.
create boolean Indicates that Seam should attempt to create a value if the context variable is missing or null. If the value attribute uses EL notation or is the name of an autocreate component, the create flag is implicitly true. The create flag cannot be used if a scope is specified. Default: false.
required boolean A flag that specifies whether or not to enforce that the value being injected is non-null. Default: true.
Scope ScopeType The context in which to look for the context variable. The scope is disregarded if the value uses EL notation. Default: UNSPECIFIED (hierarchal context search).

The value attribute on the @In annotation can be the name of a context variable or an EL value expression, or it can be omitted. If the value attribute is omitted, the most common case, the name of the context variable to search for is implied from the name of the property (according to JavaBean naming conventions). By providing a context variable name in the value attribute, the name of the context variable to search for can be different than the name of the property into which it is injected. If the value attribute uses EL notation, it is evaluated and the resolved value is injected into the property.

Common use cases for the @In annotation include injecting a persistence manager, a JSF form backing bean, or a built-in JSF component. All three types are needed by the RegisterAction component, which was created in chapter 4. Listing 6.1 shows the RegisterAction component using @In to supply all the dependent components needed to perform registration. The @Logger and @In annotations are placed inline to conserve space.

Listing 6.1. The registration component refactored to use dynamic injection

With these changes in place, you should be able to run the RegisterGolferIntegrationTest and verify that the tests pass just as before. The fixture for the test remains the same—only now, Seam wires the context variables into the component under test dynamically using bijection.

There are several permutations for how Seam resolves the context variable to inject. Let’s step through the decision process that occurs during the first phase of bijection.

6.2.2. The injection process

The decision process for performing an injection is illustrated in figure 6.3. You can see that this process has two main branches: one where the value attribute of the @In annotation is expressed in EL notation, and one where the value attribute is the context variable name or a context variable name is implied. Use this diagram to follow along with the discussion.

Figure 6.3. The decision process Seam follows when injecting a value into an @In property

Let’s begin by considering the case where the value attribute of the @In annotation is a context variable name. A few subtle differences exist between this case and when the value attribute of the annotation uses EL notation.

Seam first looks to see if a scope is specified in the annotation. If so, Seam looks for the context variable name in this scope. If a scope isn’t provided, Seam performs a hierarchical search of the stateful contexts, starting from the narrowest scope, ScopeType.EVENT,[1] and continuing all the way to the broadest scope, ScopeType.APPLICATION, as illustrated in figure 6.4. The search stops at the first non-null value that it finds and assigns that value to the property of the component. Keep in mind that a context variable can hold any value, not just a component instance.

1 Technically, the narrowest scope is the METHOD context, which is discussed in section 6.4.

Figure 6.4. The order in which the contexts are scanned when Seam searches for a context variable

If the hierarchical context search fails to resolve a non-null value for a context variable, Seam attempts to initialize a value, but only if the following two conditions are met:

  • The context variable name matches the name of a component.
  • The create flag is true on the @In annotation or the matched component supports autocreate.

If these conditions are met, Seam instantiates the component, binds the instance to a context variable, then injects it into the property marked with the @In annotation. Note that the instance may come from a factory component, covered in section 6.7.1. If the context variable name doesn’t match the name of a component, there’s nothing for Seam to create and therefore the value of the property remains null.

Before finishing its work, Seam validates against the required flag on @In. Requiring a value protects against a NullPointerException. If the required flag is true, and Seam wasn’t able to locate a value, the runtime exception RequiredException is thrown. There is one exception to this rule. Required flags are never enforced on life-cycle methods (this is true for both injection and outjection), though bijection does still take place. In any case, if the required flag is false or not enforced, the property remains uninitialized if Seam can’t locate a value.

 

Tip

If you find yourself making heavy use of the required flag to disable required injections (and later outjections), it’s an indication that you’re trying to make the component do too much. Refactor your monolithic component into smaller, more focused components and use bijection or static injection to allow them to collaborate.

 

When the value attribute uses EL notation, the semantics are much simpler. The work of locating a value is delegated to the EL variable resolver. The same validation against the required flag is performed as in the non-EL case.

If all such injections for a component succeed, the method invocation proceeds. Before you check the @In annotation from your list of Seam concepts mastered, let’s consider how the dynamic nature of the injection allows you to mix scopes and properly handle nonserializable data.

6.2.3. Mixing scopes and serializability

As you know, every time a method on a component is invoked, Seam performs the lookup to resolve a value based on the information provided in the @In annotation. This dynamic lookup allows you to inject a component instance stored in a narrow scope into a component instance stored in a wider scope. Figure 6.5 shows a narrow-scoped component instance being injected into a wider-scoped component instance at each invocation. If injection were to only happen when the wider-scoped component instance was created, the injected instance would be retained beyond the lifetime of its scope.

Figure 6.5. A narrow-scoped component is injected into a wider-scoped component.

Let’s pose this scenario as a question, plugging in actual scope names. What would happen if a request-scoped variable were to be injected into a session-scoped object via static DI? Recall that static DI occurs only once: when the object is created. In this situation, as the request-scoped variable changes between HTTP requests, the session-scoped object retains the original value of the request-scoped variable and never gets updated. This describes the behavior of most other IoC containers in existence today. By using Seam’s dynamic DI, the same property on a session-scoped component would always reflect the value of the request-scoped variable from the current request because the injection is reapplied each time the component is invoked. Additionally, the value isn’t retained beyond the end of the request since Seam breaks the reference by disinjecting the value once the method call is complete.

Let’s consider another difficult scenario that’s cleared up thanks to the dynamic nature of Seam’s DI: mixing nonserializable objects with serializable objects (a serializable object is an instance of any class that implements java.io.Serializable). If a nonserializable object is injected into the property of a serializable session-scoped object, and that property isn’t cleared prior to session passivation, the session storage mechanism will get tripped up by the nonserializable data. You could, of course, mark the field as transient so that the value would be automatically cleared, hence allowing the session to properly passivate. However, the real problem is that once the session is restored, the transient field remains null, acting as a land mine in the form of a NullPointerException that may be triggered by an unsuspecting block of code.

Applying the @In annotation to the field solves both parts of the aforementioned problem. To begin with, the value assigned through the @In annotation is cleared in the last phase of bijection, after the method on the component is invoked. Right out of the box, injections are transient without you having to add the transient keyword on all the fields marked with @In. But the real power of injection is that the injected values are restored prior to the next method call. This continuous injection mechanism allows objects coming out of passivation to be reinflated. Thus, disinjection paired with subsequent injections should alleviate a common, yet trite, pain point that arises when working with both session replication and the reactivation of a session after a server restart.

But the @In annotation isn’t the only type of injection that Seam supports. Next we look at a couple of domain-specific injection variants.

6.2.4. Injection variants

Seam supports several additional annotations for marking dynamic injection points. The two you’ll learn about in this section are @RequestParameter and @Persistence-Context. The first is used to inject an HTTP request parameter and the second an EntityManager (JPA). Technically, the Java EE container handles injecting the EntityManager into a property annotated with @PersistenceContext, but Seam follows that up with a second injection pass. Later on, you’ll learn about two more injection annotations in the section covering JSF data model selection. Of all the bijection annotations, @RequestParameter is probably the easiest to grasp, so let’s start there.

Injecting a @Requestparameter

The @RequestParameter annotation is used to inject an HTTP request parameter into the property of a component, retrieved either from the query string or the form data. You either specify a parameter name explicitly in the value attribute or let Seam imply the parameter name from the name of the property. Unlike @In, however, a value is never required.

You can, of course, inject a request parameter into a string or string array property, since request parameters are inherently strings. Going a step further, if the property’s type is not a string or string array, Seam converts the value before injecting it using the JSF converter registered for that type. If no converter is associated with the property’s type, Seam throws a runtime exception. As a reminder, JSF converters implement the javax.faces.convert.Converter interface and are registered in faces-config.xml or by adding the @Converter annotation to a Seam component class.

The @RequestParameter annotation is a convenient alternative to page parameters for creating RESTful URLs. In fact, this approach is more “Seam-like” because it uses an annotation rather than XML. The one benefit of page parameters that you lose, however, is Seam rewriting links to propagate page parameters to the next request.

In the Open 18 application, we want to create a page that displays a golfer’s profile, which will be prepared by the ProfileAction component. Let’s design it so that the ID of the golfer to display is passed to the URL using the request parameter golferId:

  http://localhost:8080/open18/profile.seam?golferId=1

The golferId request parameter can be injected into this component by placing the @RequestParameter annotation over a property with the same name. This injection takes place whenever a method on the ProfileAction component is invoked.

Therefore, the golferId property can be used in the load() method to look up the corresponding Golfer entity using the JPA EntityManager:

  @Name("profileAction")
  public class ProfileAction {
      @In protected EntityManager entityManager;
      @RequestParameter protected Long golferId;

      protected Golfer selectedGolfer;

      public void load() {
          if (golferId != null && golferId > 0) {
              selectedGolfer = entityManager.find(Golfer.class, golferId);
          }
          if (selectedGolfer == null) {
              throw new ProfileNotFoundException(golferId);
          }
     }
  }

We have the load() method invoked when the / profile.seam path is requested by making this method a page action for the corresponding view ID in the page descriptor:

  <page view-id="/profile.xhtml">
    <action execute="#{profileAction.load}"/>
  </page>

The motivation for using a page action is to ensure that the profile can be retrieved before committing to rendering the page. If an instance of Golfer is found, it’s stored in the selectedGolfer property. However, if the golferId request parameter doesn’t produce a Golfer instance, a custom runtime exception named ProfileNotFoundException is thrown. This exception causes Seam to throw up a 404 error page, along with a message stating that the profile could not be found, which happens because the exception class is annotated with @HttpError:

  @HttpError(errorCode = HttpServletResponse.SC_NOT_FOUND)
  public class ProfileNotFoundException extends RuntimeException {
      public ProfileNotFoundException(Long id) {
          super(id == null ? "No profile was requested" :
              "The requested profile does not exist: " + id);
      }
  }

That’s as far as we go with this example right now. You still need to learn how to promote the value of the selectedGolfer property to a context variable that can be accessed from the view, which the next section covers.

Seam isn’t alone in its support for dynamically injecting values into annotated properties. Java EE has its own set of annotations for declaring what the spec terms a “resource injection.” For the most part, Seam stays out of the way and lets the container handle the task. But Seam does get its hands dirty with the @Persistence-Context annotation.

Augmenting the @PersistenceContext

The @PersistenceContext annotation is a Java EE annotation that marks a property on a Java EE component that should receive a container-managed EntityManager resource injection. After the EntityManager is injected by the Java EE container but before the method invocation proceeds, Seam wraps the EntityManager in a proxy object and injects it again. The proxy adds EL value expression support to Java Persistence Query Lanaguage (JPQL) queries. But otherwise, the life cycle of the Entity-Manager is controlled by the Java EE container. The @PersistenceContext annotation is explained more in Chapter 8 and the EntityManager proxy in Chapter 9. Keep in mind that this annotation is only relevant for Java EE managed components (e.g., JSF managed beans or EJB session beans).

Let’s pause for a moment to take a look around because you are now standing at the ridge of where DI ends and bijection continues. Looking behind you, you see that bijection has evolved DI by making the injection dynamic. Looking forward, you see that there’s a whole other side of this pattern that has never before been explored. It’s the flip side of injection known as outjection. Let’s explore it!

6.3. @Out-jecting context variables

Outjection is a way of pushing state held by a component out to the Seam container. You can think of a component as the parent and the properties as its children. Outjection is like sending the child off (perhaps kicking it out) to live in the world on its own. The parent (the component) exports the child (the property) to a new address (the context variable). Other people (components) can visit that child at the new address without consulting the parent. The property value is now associated with its own context variable that puts it on equal footing with other components in the Seam container.

An outjection point is declared by adding the @Out annotation, summarized in table 6.2, to a bean property—a field or JavaBean-style “getter” method. After a component method is invoked, the value of the property is used to create a context variable or bind to one that already exists. If a name isn’t provided in the value attribute, the name of the property is used. As with the @In annotation, the value attribute on the @Out annotation can be used to make the name of the context variable different from the property name. Note that the @Out annotation doesn’t support EL notation as @In does.

Table 6.2. The @Out annotation

Name:

Out

Purpose:

Instructs Seam to assign the value of the property to the target context variable after a method on the component is invoked

Target:

METHOD (getter), FIELD

Attribute

Type

Function

value String The name of the context variable to which the value of the property should be bound. Default: the property name.
required boolean A flag that indicates whether to enforce that the value to be outjected is non-null. Default: true.
scope ScopeType The context in which to store the context variable. Default: the scope of the target component or the scope of the host component if the context variable name isn’t the name of a component.

The outjection process is not as complex as the injection process. However, there are still decisions Seam must make before assigning the value of the property to a context variable.

6.3.1. The outjection process

Figure 6.6 shows the outjection process. As you can see, three main decisions are made in this process: the context variable name to use, the scope in which to store the context variable, and whether to permit a null value. Let’s step through this process.

Figure 6.6. The decision process Seam follows when outjecting the value from an @Out property

The trickiest part to understand is how Seam infers a scope if one is not stated explicitly. If you specify a scope in the @Out annotation, Seam assigns the value of the property to a context variable in that scope. The name of the context variable is either the name of the property or the override specified in the value attribute of the @Out annotation. A context variable can’t be bound to the stateless context, so @Out doesn’t permit use of this scope.

If a scope isn’t specified, Seam first attempts to locate a component with the same name as the target context variable. If one exists, and its type is equivalent to the property’s type, the property value is outjected to the scope of that component. Note that the matching component may have been defined using the @Role annotation. Roles effectively centralize the target scope of an outjection, rather than defining it at the outjection point. If the context variable name doesn’t match the name of a component (or component role), Seam uses the scope of the host component (i.e., the component on which this property resides). If the scope of the host component is stateless, Seam chooses the event scope instead.

In the final step, Seam processes the required flag. If the required flag is true (the default) and the value being outjected is null, Seam throws the runtime exception RequiredException. Recall that required flags are implicitly false on life-cycle methods.

Let’s look at a couple of use cases where outjection is useful.

6.3.2. Outjection use cases

Outjection is useful for accomplishing two goals. You can use it to expose the model to the view as a result of executing an action method, or you can use it to push data into a longer-term scope so it’s available on subsequent requests. Let’s look at the two cases in turn.

Preparing the model for the view

The view isn’t very useful without data to display. Preparing this data is typically the responsibility of the page action or the action method triggered from the previous page. In Struts, you usually pass the model to the view by assigning values to HttpServletRequest attributes in the action. Managing variables in this way can be quite cumbersome and couples your code tightly to the Java Servlet API.

Before abandoning the Servlet API, let’s find a compromise that loosens this coupling and makes the code less cumbersome. In Chapter 4, you learned that Seam normalizes the servlet contexts under a single API. You can inject one of these contexts directly into a Seam component using bijection. A context behaves like a map, where the keys are the context variable names. You can add a context variable to the event scope with the following setup:

  @In protected Context eventContext;

  public void actionMethod() {
      eventContext.set("message", "Hello World!");
  }

Although this approach works, there’s a cleaner, more declarative way of accomplishing the same task. You simply mark properties with the @Out annotation that you want to have placed into the target context and your job is done. Here’s the same code taking a declarative approach:

  @Out(scope = ScopeType.EVENT) protected String message;

  public void actionMethod() {
      message = "Hello World!";
  }

Here you see that outjection decouples the context variable assignment from the business logic in the action. (The scope attribute on the @Out annotation is only required if the target scope is different from the scope of the host component.) As far as the view is concerned, it doesn’t care how the context variable was prepared.

Let’s complete the golfer profile example started earlier by making the selected golfer available to the /profile.xhtml view. The goal is to create the output shown in figure 6.7.

Figure 6.7. A golfer’ profile page. The data is supplied by an outjected context variable.

To extract the value of the selectedGolfer field from the ProfileAction component when the load() method is invoked, we add the @Out annotation above the field. The value of the field is then assigned to the equivalently named context variable in the event context:

  @Name("profileAction")
  public class ProfileAction {
      @Out protected Golfer selectedGolfer;
      ...
      public void load() { ... }
  }

The selectedGolfer context variable can then be referenced in value expressions in the /profile.xhtml page as shown here:

  <h1>#{selectedGolfer.name}<h1>
   <rich:panel>
     <f:facet name="header">Profile</f:facet>
     <s:decorate template="layout/display.xhtml">
       <ui:define name="label">Gender</ui:define>
       #{selectedGolfer.gender}
   </s:decorate>
   <s:decorate template="layout/display.xhtml">
     <ui:define name="label">Birthday</ui:define>
     <h:outputText value="#{selectedGolfer.dateOfBirth}">
       <s:convertDateTime pattern="MMMM dd , yyyy"/>
     </h:outputText>
   </s:decorate>
   ...
  </rich:panel>

The other use case for outjection is to propagate state.

Keeping data in scope

Just because a field is outjected doesn’t mean it has to be used in the view. It’s quite reasonable to outject a value just so that it can be injected into a component on a subsequent request. Say goodbye to hidden form fields and the mentality of having to manually propagate variables from one request to the next. Instead, you simply use outjection to put a context variable on a shelf (a long-term scope such as the page, conversation, or session scope) and pull it down when you need it again. In this case, outjection decouples the mechanism of preserving state from the business logic. This technique is especially useful in conversations, which are covered in Chapter 7.

 

Storing component instances out of Seam’s reach

You should not store a reference to a component instance in a place where Seam can’t manage it, such as inside a collection. It should only be stored in a context variable.

An object ceases to be an instance of a Seam component when you manage it yourself. While storing it out of Seam’s reach won’t cause an error, Seam stops intercepting its methods, and thus services such as bijection and events are no longer applied to it.

If you need collection semantics, you should store the name of the component instead, then look up each instance when you need it using Component.getInstance().

 

When you began reading this chapter, you may have thought bijection a bit mysterious. However, with the information covered up to this point, I doubt you’ll forget how bijection works. If for some reason you do, just remember that injections happen before a component method is invoked and outjections happen after the method call is complete. Lastly, the injections are cleared just before the method returns. Repeat it to yourself. That way, when you see the error message “@In attribute requires non-null value” or “@Out attribute requires non-null value,” you’ll know what Seam is trying to tell you. If all else fails, just ask your golf caddy. Now let’s use bijection to make lists in JSF “clickable.”

6.3.3. Built-in @DataModel support

In JSF, UIData components, such as <h:dataTable>, are backed by a special collection wrapper called a data model. A data model is a way for JSF to adapt various types of collections to the UI component model in a consistent way and to support capturing a row selection made by the user. The collection wrappers extend from the abstract class javax.faces.DataModel and support the major collection types. Seam builds on this set by adding support for the Query component from the Seam Application Framework. The Query component manages the result of a JPQL/HQL query, which Seam retrieves and wraps in a ListDataModel. You’ll learn about the Query component in chapter 10. The mapping between the collection types and their wrappers is shown in table 6.3.

Table 6.3. The corresponding JSF data model wrapper for each type of collection

Native Collection

JSF DataModel Wrapper javax.faces.model.*

java.util.List ListDataModel
Array ArrayDataModel
java.util.Map MapDataModel
java.util.Set SetDataModel
org.jboss.seam.framework.Query ListDataModelwraps Query#getResultList()

So what do these wrappers have to do with bijection? To properly prepare collection data to be used in a UIData component, you should wrap it in a JSF data model. Why should you have to deal with this drudgery in your business logic? This task sounds like something the framework should handle. Seam developers agree. For this purpose, they created the @DataModel annotation, summarized in table 6.4. The @DataModel is used in place of the @Out annotation for outjecting one of the collection types listed in table 6.4. Before the value of the @DataModel property is assigned to the context variable during outjection, it’s first wrapped in the correct JSF DataModel implementation.

Table 6.4. The @DataModel annotation

Name:

DataModel

Purpose:

Wraps a collection in a JSF DataModel and outjects the resulting value

Target:

METHOD (getter), FIELD; must be one of the collection types in table 6.3

Attribute

Type

Function

value String The name of the context variable under which the value of the wrapped collection should be stored. Default: the property name.
scope ScopeType The context in which to place the wrapped collection. The only permissible value is ScopeType.PAGE. Default: inherits scope from component or ScopeType.EVENT if component is stateless.

Let’s use Seam’s data model support to display a list of newly registered golfers on the home page, which will be made clickable so that a user can view the golfer’s profile. The goal of this section is to produce the output shown in figure 6.8.

Figure 6.8. The list of new golfers prepared as a JSF data model as a result of outjection

The business logic is responsible for fetching the list of new golfers. We let Seam handle the details of wrapping that collection in a JSF DataModel and exposing it to the view by adding the @DataModel annotation, a variant of the @Out annotation, to the newGolfers field:

  @Name("profileAction")
  public class ProfileAction {
      @DataModel protected List<Golfer> newGolfers;
      ...
  }

Of course, that’s not enough for the newGolfers field to be outjected—heck, it’s not even populated. First, we need a method that populates this collection. Then, we need to figure out how to activate outjection. Let’s reuse the ProfileAction component. We add the method findNewGolfers() to load the list of new golfers from the database. To keep things interesting, we perform a little quick and dirty randomization on the list:

  @Name("profileAction")
  public class ProfileAction {
      protected int newGolferPoolSize = 25;
      protected int newGolferDisplaySize = 5;

      @Out(required = false) protected Golfer selectedGolfer;
      @DataModel protected List<Golfer> newGolfers;
      ...
      public void findNewGolfers() {
          newGolfers = entityManager
              .createQuery(
                  "select g from Golfer g order by g.dateJoined desc")
              .setMaxResults(newGolferPoolSize)
              .getResultList();

          Random rnd = new Random(System.currentTimeMillis());

          while (newGolfers.size() > newGolferDisplaySize) {
              newGolfers.remove(rnd.nextInt(newGolfers.size()));
          }
      }
  }

As you’ve learned, when a method on a Seam component is executed, such as findNewGolfers(), bijection takes place. That means once the invocation of this method is complete, the value of the newGolfers field is going to be wrapped in a JSF DataModel and outjected to the equivalently named context variable of the event scope. The event scope is chosen since that is the scope of the host component and the scope isn’t explicitly specified.

Notice that the required attribute on the @Out annotation above the selectedGolfer property is now set to false. This directive is necessary since the findNewGolfers() method doesn’t assign a value to selectedGolfer and bijection would otherwise complain that it isn’t set. This situation often arises when you use the same component for more than one purpose. If you find yourself making excessive use of the required directive, that’s an indication that the component is trying to do too much and that you should divide it into smaller, more focused components.

Once the findNewGolfers() method is invoked, the JSF view will have access to the collection of new golfers using the value expression #{newGolfers}. The UIData component that renders the list of golfers (in this case, <rich:dataList>) sees a ListDataModel rather than the java.util.List:

 <rich:panel>
   <f:facet name="header">Cool New Golfers</f:facet>
   <rich:dataList var="_golfer" value="#{newGolfers}">
     #{_golfer.name}
   </rich:dataList>
 </rich:panel>

 

Tip

You may notice in this code snippet that I prefix the iteration variable (the golfer) with an underscore (_). I recommend this coding style to make it clear that the variable is local to the iteration loop and to avoid conflicts with existing context variables.

 

The only remaining question is, “When is the findNewGolfers() method invoked?” We want to execute this method eagerly when the home page is requested, not as a result of a user-initiated action. Once again, we use a page action:

  <page view-id="/home.xhtml" action="#{profileAction.findNewGolfers}"/>

Now, whenever the home page is requested, the findNewGolfers() method prepares the newGolfers context variable for the view. With the @DataModel annotation, you never have to think about the JSF DataModel interface; it’s completely transparent. Seam even takes care of reinitializing the context variable whenever a change is detected in the underlying collection. Your component is free to use properties that are native Java collection types. Your UI component sees the outjected data as the appropriate DataModel wrapper class. Where this pays off is in capturing a row selection from the DataModel, again without having to tie your component to the JSF API.

Making a @Datamodelselection

Presented with a list of golfers, the user of the application will want to interact with that list. One common use case is “clickable lists.” To continue with our example, the user clicks on the name of one of the golfers in the table and the application brings up the golfer’s profile. This mechanism is a contrast to using a RESTful URL, though the two can coexist.

In a clickable list, the user is performing an action on the data associated with the row that receives the event. One such action is drilling down, which is demonstrated here. Other actions include deleting and editing. The only limitation is that this mechanism can be applied to only a single row at a time.

How do you know which row was clicked? Seam works in conjunction with JSF to take care of these details. One of the reasons for using a DataModel in a UIData component is so that JSF can correlate an event triggered from a row in the table with the data used to back that row. Seam can take the data from the selected row and inject it into the component receiving the action using one of two @In-style annotations.

Clickable lists are effortless in Seam thanks to the @DataModelSelection annotation. The @DataModelSelection annotation injects the value of the selected row in a collection previously outjected by the @DataModel annotation and assigns it to the corresponding component property. Seam also supports capturing the index of the selected row using the @DataModelSelectionIndex annotation. You place either of these two annotations, summarized in table 6.5, on a separate property of the same component in which the @DataModel annotation is defined. Of course, the property annotated with @DataModelSelection must have the same type as the items held in the collection, while the property annotated with @DataModelSelectionIndex must be a numeric type.

Table 6.5. The @DataModelSelection and @DataModelSelectionIndex annotations

Name:

DataModelSelection/DataModelSelectionIndex

Purpose:

Captures the selected row (or row index) of the corresponding JSF DataModel

Target:

METHOD (setter), FIELD

Attribute

Type

Function

value String The context variable name of the DataModel. Default: the name of the @DataModel property on the same component, if there is exactly one.

Seam will put two and two together and assign the value of the selected row in the UI to the property annotated with @DataModelSelection, or the index of the selected row to the property annotated with @DataModelSelectionIndex. This selection occurs by triggering an action on a row of a UIData component such as <h:dataTable>.

Let’s build on the example of the list of new golfers by first making the names clickable. First, we enhance the ProfileAction component to include a method named view() that will be used to select a golfer from the new golfer list. This method will eventually be bound to a UI command component to enable the selection:

  <h:commandLink value="#{_golfer.name}" action="#{profileAction.view}"/>

The implementation of the view() method is shown in listing 6.2, along with several changes to the class that are highlighted in bold to support this new use case. The RESTful logic presented earlier is left intact so that both use cases can be supported.

Listing 6.2. A component that supports a clickable list of golfers

An important change had to be made to the ProfileAction component to support clickable lists. The @DataModel property is now scoped to the page context (i.e., ScopeType.PAGE) rather than the event context. In order for @DataModelSelection to capture the selected row, the original @DataModel collection must be available on postback. If the collection changes, it may cause the wrong row to be selected. This happens because the row index captured by the event no longer points to the correct row in the collection on postback. If the collection disappears altogether, the result will be a “ghost click” (see the accompanying sidebar). The latter happens because the portion of the JSF component tree that held that event is discarded and thus the event doesn’t fire. Regardless of how hard Seam searches, it won’t be able to locate the data model selection and the @DataModelSelection and @DataModelSelectionIndex annotations are helpless. UIData components (e.g., <h:dataTable>) depend on the underlying data to be stable during subsequent requests. To guarantee that the collection remains stable, you can store it in the UI component tree by scoping @DataModel to the page context, as we’ve done in this example. Always remember this point when dealing with UIData components. You have to get your DataModel to make the leap from render to postback. In the next chapter you’ll learn how conversations are even better suited to deal with this problem since they can carry data across an arbitrary number of postbacks and even non-JSF requests.

Finally, we add an <h:commandLink> around the name of each golfer, which will invoke the view() method on ProfileAction when clicked:

  <h:form>
    <rich:panel>
      <f:facet name="header">Cool New Golfers</f:facet>
      <rich:dataList var="_golfer" value="#{newGolfers}">
        <h:commandLink value="#{_golfer.name}"
          action="#{profileAction.view}"/>
      </rich:dataList>
    </rich:panel>
  </h:form>

 

Ghost clicks

One utterly frustrating problem of JSF that Seam alleviates is the ghost click. A ghost click happens when the portion of the UI component tree that queued a UI event is dropped.

Upon reading this explanation, you might be thinking, “Isn’t that a bug?” Sadly, it isn’t. Certain branches of the UI component tree are processed dynamically. If the structure of the tree isn’t reproducible, there’s a chance that the behavior will be lost. An example of where this can happen is in any branch controlled by a component in the UIData family. The UIData components are unique because they are data driven. Let’s consider how UIData components are handled and how it can result in portions of the tree being dropped.

JSF walks the UI component tree during rendering. When it encounters a UIData component, it iterates over the collection in the DataModel that’s bound to the component to render each row. The component tree is then saved in the session or serialized and sent along with the response. Although the data is a critical part of how UIData components operate, JSF stores only the UIData components themselves, not the underlying data.

When the component tree is restored on postback, JSF walks the UI component tree again, looking for events (among other things). Events triggered on a UIData component are associated with the index of the activated row. When JSF arrives at the UIData component, it again iterates over the data model to process each row, lining up events with the row index. If the data model changes prior to the restore stage, the event may be correlated with the wrong row. Worse, if the data disappears, any event associated with that portion of the tree is silently discarded. The result is a ghost click.

The lesson here is that JSF expects the collection wrapped by the DataModel to remain stable between render and postback. The easiest way to guarantee the stability of the collection is to store it in the UI component tree. In Seam, you store a context variable in the UI component tree by scoping it to the page context. This approach accomplishes the same task as the <t:saveState> tag from the Tomahawk project. As another option, third-party UIData components typically offer a built-in “save state” attribute. The most flexible option is to bind the data model to a long-running conversation.

To truly appreciate how maddening a ghost click can be, you have to be the victim of it. The profile example in this chapter gives you an opportunity to research the problem in a lab environment where your job—or contract—isn’t on the line.

 

When the view() method is invoked, Seam takes the selected row from the @DataModel collection, newGolfers, and injects it into the corresponding @DataModelSelection property, selectedGolfer. Seam makes this association automatically since both properties reside on the same component. If there were more than one @DataModel property on the component, it would be necessary to specify the context variable name used by the @DataModel property in the value attribute of the @DataModelSelection annotation in order for the correlation to be made. Failure to make this distinction results in a runtime exception. The @DataModel property and the @DataModelSelection property must always reside on the same component.

In JSF, you have to use a command link or button to perform a data model selection using the pattern just described. That means a POST form submission occurs. What’s worse is that JSF submits the form using JavaScript. If you don’t care how the job gets done, the combination of a page-scoped @DataModel, @DataModelSelection, and a JSF command component is a grand slam. However, if you need to allow the user to open the data model selection link in a new tab or window, you may need to take the solution a step further. Currently, if the user right-clicks on the golfer name and tries to open the profile in a new tab or window, the home page will load rather than the profile page. To allow users to achieve the desired result, the data model selection must be passed along with the URL. JSF doesn’t support this feature, but Seam does.

Data model selection with Seam command components

The command components in the Seam UI component library, <s:link> and <s:button>, support data model selections, despite the fact that they don’t submit a form or restore the JSF UI component tree like the JSF command components. In previous chapters, you learned that it’s possible to use the Seam command components to execute actions and perform navigation, so you know they are already quite capable. They can also emulate the data model selection feature of JSF through the use of two URL parameters, dataModelSelection and actionMethod, which are processed by the Seam phase listener and used to inject the data from the selected row into a @DataModelSelection or @DataModelSelectionIndex property. An example is provided in a moment.

The Seam command component tags aren’t quite a drop-in replacement for the JSF command components. They don’t restore the JSF UI component tree, which means that the page-scoped data isn’t propagated. For our example, this means that the page-scoped newGolfers data model is left behind. Thus, to allow formless data model selection using the Seam command components, the data model must be stored in a longer-lived scope.

There are two synchronized scopes that you can use as an alternative to the page scope: session and conversation. Generally, I recommend using the conversation scope. But since we haven’t explored conversations—and by that I mean long-running conversations—we go with the session scope for now. I encourage you to come back and play with this example after you have mastered conversations in Chapter 7.

Your first instinct might be to set the scope of the @DataModel annotation to ScopeType.SESSION. However, that won’t work in this case since you can’t explicitly outject a data model to the session scope (only to the page scope). Therefore, it’s necessary to put the ProfileAction component in the session scope so that the @DataModel annotation inherits the session scope from the component:

  @Name("profileAction")
  @Scope(ScopeType.SESSION)
  public class ProfileAction {
      @DataModel
      protected List<Golfer> newGolfers;
      ...
  }

You can follow this change with a switch from the <h:commandLink> tag to <s:link>:

  <s:link value="#{_golfer.name}" action="#{profileAction.view}"/>

An example of a URL generated by this component tag is as follows:

  /open18/home.seam?dataModelSelection=_golfer:newGolfers[0] &actionMethod=home.xhtml:profileAction.view

The URL indicates that the first entry in the newGolfers collection is to be used as the data model selection and that the view() method on the component named profileAction is to be executed. The user is then directed to the view ID returned by view(). If view() were to return an outcome value rather than a view ID, JSF would consult the navigation rules to determine the next view.

These extra steps to support the Seam command components may seem like too much work, in which case you may just want to stick with the JSF command components. You may want to consider ways to select a row in a @DataModel context variable without the use of @DataModelSelection.

Other approaches to data model selection

As an alternative to using the @DataModelSelection annotation, you can pass the context variable of the current row to the action method using a parameterized method expression, supported by the JBoss EL. To use this feature, the data model must remain available until the next request, as with the data model selection. Again, the options are page, session, or conversation scope with a long-running conversation. With the scope of the data model set appropriately, you can pass the _golfer context variable as an argument to the view() action method as follows:

  <h:commandLink value="#{_golfer.name}"
    action="#{profileAction.view(_golfer)}"/>

The signature of the action method would need to change to accept the parameter:

  public String view(Golfer golfer) {
      this.selectedGolfer = golfer;
      return "/profile.xhtml";
  }

The benefit of using the parameterized EL is that it allows you to pass a property of the row data rather than the row data itself. For instance, you could pass the golfer’s username instead:

  <h:commandLink value="#{_golfer.name}"
    action="#{profileAction.view(_golfer.username)}"/>

To go in a completely different direction, you could use a RESTful URL to select the golfer. In this case, you’re using the @DataModel just for rendering purposes, so it doesn’t need to survive across the redirect. Instead, the information required to retrieve the golfer is placed directly in the URL using a link parameter:

  <s:link value="#{_golfer.name}" view="/profile.xhtml">
    <f:param name="golferId" value="#{_golfer.id}"/>
  </s:link>

After having seen these options, you have to decide for yourself whether you like the convenience of the JSF data model selection or whether you prefer the RESTful URL. Unfortunately, the @DataModelSelection is really just a hindrance when you’re trying to create a RESTful URL. My advice is that unless you have a strong use case for supporting a RESTful URL, you should leverage the productivity gain that the page-scoped @DataModel, @DataModelSelection, and JSF command component combination affords you.

Bijection can be powerful and convenient, but if applied at the wrong time, it can throw a wrench into the works. In the next section, you’ll learn how to exert some control over when bijection is used and when Seam applies this reservation automatically.

6.4. Bypassing bijection

Bijection is one of the most powerful and compelling features of Seam. But with great power often comes great confusion. It’s just as valuable to know when bijection is not used as to know when it is. In this section, we look at which method calls do not trigger bijection and also how to disable it when it’s getting in your way.

6.4.1. Internal method calls

As established earlier, bijection is implemented as a method interceptor. Method interceptors are applied around method calls that are invoked on proxy objects. When you ask the Seam container for a component instance—perhaps through an EL expression or an injection—what you get back is a proxy of the instance. Therefore, any method call invoked on that proxy is going to pass through the method interceptors and, in turn, trigger bijection.

However, method interceptors are blind to what goes on within the target method. As far as the interceptor is concerned, the target method is a black box. Inside of the intercepted method, you’re dealing with the raw instance of the component when you refer to the implicit variable this. Local method calls (i.e., methods on the same class) aren’t observed by the method interceptors, and therefore, bijection isn’t wrapped around them. If you have a strong grasp of how method interceptors work, this fact should come as no surprise to you. However, for those with less exposure to method interceptors, the distinction between the two circumstances may not be so obvious.

Let’s return to the RegisterAction component to see an example of an internal method call. We want to verify that the username chosen by the new golfer is available. The method isUsernameAvailable() on RegisterAction performs the check and emits an error message if the username is already in use. The modified RegisterAction component is shown in listing 6.3 in abbreviated form. Note that the internal call to the isUsernameAvailable() method doesn’t trigger bijection.

Listing 6.3. An internal method call that checks whether the username is available

Just remember that when a component is asked to do work by some other part of the system, the communication takes place through the proxy, so bijection will occur before and after the method call. However, any method that the component invokes on itself—an internal method call—isn’t going to benefit from or be afflicted by any interceptor-provided functionality, such as bijection.

I chose the words “benefit” and “afflicted” in that last statement for a reason. There are times when you need method interceptors to be applied on a method, even if that method happens to reside on the same component. There are also circumstances when allowing the method interceptors to execute would be problematic. First, I’ll explain how to get a reference to the proxy of the current instance to make the method call appear as if it’s originating from outside the component. Then, you’ll learn why Seam skips bijection if the method is reentered while execution of the target method is still proceeding.

6.4.2. The mystical method context

I’ll admit that I omitted details earlier when I said that the event scope is the narrowest scope in Seam, but trust that it was for your own good. In truth, it’s because the narrowest scope, method, is intended strictly for internal use. So technically, the event scope is the narrowest public scope and I get to retain my integrity.

If the method scope is part of the internal Seam API, why mention it? I bring it up to raise awareness of its impact and to transition to my next point. Before a method call on a component begins, Seam binds the unproxied instance of the component to the component name in the method context. After the method call, this context variable is cleared. The reason this assignment is done is to avoid invalid behavior in the case of recursive calls. But don’t worry about the details; let’s look at the consequence.

As you are now fully aware, interceptors aren’t applied to internal method calls. For that, you need to invoke the component instance the way the rest of the system sees it (i.e., through the proxy). Recall from Chapter 4 that you can look up a component instance using the Seam API as follows:

  (RegisterAction) Component.getInstance("registerAction")

Normally, this call would give you the component instance and its entourage of method interceptors. However, if this call is made from within the RegisterAction component, the unproxied instance is returned instead since it’s found in the narrowest scope, method. So even if you invoke the isUsernameAvailable() method on the result of this lookup, the method interceptors won’t be applied:

  ((RegisterAction) Component.getInstance("registerAction"))
      .isUsernameAvailable(newGolfer.getUsername())

However, you can get the proxied instance by specifying the component’s context:

  (RegisterAction) Component.getInstance("registerAction", ScopeType.EVENT)

It’s very important to keep this little tool in your emergency kit because I can assure you that a time will come when you need to get a handle on the proxy and, in turn, trigger the interceptors.

With the method context out in the open, it’s time to move on to the next point: reentrant method calls. Believe it or not, even if you looked up the RegisterAction component using the trick I just showed you and called the isUsernameAvailable() method on it, bijection still wouldn’t be applied. Read on to discover why.

6.4.3. Reentrant method calls

A reentrant method call is as simple as it sounds: the component is reentered while it’s in use. To be more specific, a method is called on the proxy while a method on the instance is still executing. You just saw an example of this in the previous section. Within the RegisterAction component, the instance of the RegisterAction component was retrieved through the Seam API and a method called on it:

  (RegisterAction) Component.getInstance("registerAction", ScopeType.EVENT)
      .isUsernameAvailable(newGolfer.getUsername())

You might also see this situation if you’re implementing a visitor or double dispatch pattern, where the collaborator component reaches back through the proxy to execute a method on the component. So what does all this have to do with bijection? One of the golden rules of bijection reads as follows:

At no time during the execution of a component’s method is bijection applied to that component a second time.

This point goes back to the mechanics of bijection, which taught you that bijection encompasses a method call and doesn’t do anything while the method is executing. Bijection occurs before and after every method call on a Seam component instance, but only if the method call isn’t a reentrant method call.

The motivation for skipping bijection on reentrant method calls is twofold:

  • It reduces overhead.
  • It keeps the state of the component instance stable and consistent.

As for the first point, there’s no reason to apply injections again if they’ve already happened. However, there’s hardly any reason to mention the first point in view of how critical the second point is. Bijection alters the state of the instance. Although injection might simply be redundant, or at worse inject a different value than what was injected originally, that’s not the biggest impact. The most drastic change occurs in the final step of bijection: disinjection, which wipes away the injections that were applied. If this were to happen while the original method call is still executing, it would have a disastrous effect (i.e., NullPointerException mania). So it’s important that Seam be cognizant of the reentrant method call and not allow it to trigger bijection. Consequently, this also explains why method invocations on Seam components must be synchronized. Otherwise, two threads calling the same component simultaneously would cause the problem just described. If you can’t synchronize the call, don’t use bijection.

Let’s consider a simple example from Open 18 to illustrate when this situation may occur in a more natural scenario. Before a golfer can register, two validation checks must be performed. The application needs to ensure that the username requested by the registering golfer is available and that the email address entered isn’t already in use to avoid a duplicate registration. Let’s introduce the GolferValidator component, to which these validations can be delegated. However, the GolferValidator is just a coordinator. The validation checks just described are hosted on the RegisterAction component. So we have a double dispatch going on here. Rather than getting bogged down in the code, let’s look at a diagram. Figure 6.9 illustrates how three method calls are made to the RegisterAction component when the user submits the registration form, yet bijection is only applied to the register() method call.

Figure 6.9. An example of two reentrant method calls on the RegisterAction component made by the collaborator component GolferValidator

Setting the log level to TRACE reveals that Seam acknowledges the reentrant call:

  intercepted: registerAction.register
  intercepted: golferValidator.validate
  intercepted: registerAction.isUsernameAvailable
  reentrant call to component: registerAction (skipping bijection)
  intercepted: registerAction.isEmailRegistered
  reentrant call to component: registerAction (skipping bijection)

This discussion of reentrant method calls is technical, but it’s important to understand why Seam forgoes the use of bijection even if a method is being invoked on the proxy of the component instance.

With the exception of JPA entity classes, bijection is applied to all Seam components. However, certain isolated components have no use for bijection. In these cases, you likely want to forgo bijection outright. You can tell Seam not to intercept method calls on a component using a special annotation.

6.4.4. Disabling bijection by disabling interceptors

Since bijection is provided by a method interceptor, you can avert bijection by disabling the interceptors on a single method or on the entire component. You instruct Seam not to apply interceptors by adding the @BypassInterceptors annotation, summarized in table 6.6, at the class or method level. The method-level annotation is a good way to optimize one area of the code without cutting out the benefits of interceptors from the component as a whole.

Table 6.6. The @BypassInterceptors annotation
Name: BypassInterceptors
Purpose: If applied to a method, disables all the interceptors that are wrapped around a single method call. If applied to a component, it has the same effect as applying it to every method on the component.
Target: TYPE (class), METHOD

Unfortunately, if you disable interceptors, you end up stripping away all the other interceptor-based functionality, not only bijection. Such features include declarative conversation controls, transaction management, and event handling, to name a few. Just be aware that by disabling interceptors, you may get more than you bargained for.

 

Note

Adding @BypassInterceptors to a component doesn’t disable life-cycle methods. Life-cycle methods are those annotated with @Create, @PostConstruct, @Destroy, or @PreDestroy that are covered in section 4.5.5 of Chapter 4.

 

What’s the motivation for disabling interceptors? By no means are interceptors punishing to the runtime execution speed of your code, but they do consume extra cycles. If you know that you don’t need the functionality provided by interceptors, you might as well disable them. If you’re using a Spring bean as a Seam component, explained in Chapter 15 (online), disabling Seam’s interceptors makes sense if equivalent functionality is already covered by Spring’s interceptors.

In the Open 18 application, the PasswordBean component has no use for interceptors as it merely holds data and validates its internal state. It’s safe to disable interceptors entirely:

  @Name("passwordBean")
  @BypassInterceptors
  public class PasswordBean { ... }

Another useful place to disable interceptors is on core object methods such as toString():

  @BypassInterceptors
  public String toString() {
      return new StringBuffer()
          .append(getClass().getName()).append("[")
          .append("password=").append(password) .append(",")
          .append("confirm=").append(confirm)
          .append("]").toString();
  }

The toString() method, as well as equals() and hashCode(), don’t benefit from the functionality provided by the interceptors. If anything, the interceptors just get in the way. For instance, required values enforced by @In and @Out might cause the toString() method to be aborted, which can mask real problems when you’re trying to debug.

While interceptors can sometimes be overzealous, the functionality they provide is essential for decoupling business logic from boilerplate lookup code. One such area where interceptors help to simplify code and cut out tight coupling is when a component wants to send a notification to other components so they have a chance to act.

6.5. Component events

Dynamic injection helps decouple components because it eliminates explicit lookups for dependent objects. However, the components still interact with one another directly. This arrangement works well when the components are all working to accomplish a common goal, as is the case with the registration example. However, in cases where tangential logic must be performed, perhaps even to spawn an asynchronous operation, events may be a better option. Events offer a way for components to pass messages to one another. They provide separation of concerns, make components easier to test in isolation, and can even remove a hard dependency on an API such as JSF.

Events can be raised by a component or generated during any page-related activity. These events are passed through the Seam container, which acts as the messaging mediator. Seam’s event support works much like a messaging service such as JMS. There are both producers and consumers. When an event is raised, it’s posted to the Seam container. Seam then looks for registered observers for that event and notifies them by executing the registered methods.

6.5.1. Raising an event from a component

Events can be raised from within a Seam component either using the Seam Events API—the built-in Events component—or using an annotation. The Events API offers the most flexibility, but it does tie your code to that API. The annotation @RaiseEvent, on the other hand, allows you to control event creation declaratively.

Using the Events Api

The Events API supports passing an arbitrary number of parameters to the observer when an event is raised. Passing data gives the observer a chance to access the data available within the scope of where the event was raised without having to be present.

The default behavior is to notify observers of the event immediately. It’s also possible to schedule events. Seam supports both synchronous and asynchronous scheduling. In the synchronous case, you can schedule that an event be fired when a transaction completes or only when it completes successfully. The asynchronous scheduler allows you to supply either an EJB 3 Timer schedule or a Quartz Cron schedule, depending on which asynchronous dispatcher you’re using in your application.

Let’s consider an example. The marketing department has requested that we gather statistics when members register and when they cancel the registration form so the effectiveness of the registration process can be determined. The requests from marketing are ever-changing. Rather than disrupt our nicely tested registration logic, we want to put this extra logic in an observer. That way, when the next feature request is handed down from marketing, we can just take on another observer, once again avoiding disrupting working code.

Modify the register() method of the RegisterAction component to raise the golferRegistered event once the new golfer has been persisted successfully:

   public String register() {
       ...
       entityManager.persist(newGolfer);
       Events.instance().raiseEvent("golferRegistered");
       ...
       return "success";
   }

If we want to ensure that the logic performed by the collateral code operates in a separate transaction (so as not to jeopardize the transaction used to persist the new golfer), we can schedule this event to be fired after successful completion of the current transaction:

  @In private Events events;
  public String register() {
      ...
      entityManager.persist(newGolfer);
      events.raiseTransactionSuccessEvent("golferRegistered");
      ...
      return "success";
 }

You can also pass any number of parameters to the observer:

  public String register() {
      ...
      entityManager.persist(newGolfer);
      events.raiseTransactionSuccessEvent("golferRegistered", newGolfer);
      ...
      return "success";
  }

The observer could get a handle on the newGolfer using bijection, but the event parameter offers more clarity and, thus, it’s easier for a fresh set of eyes to understand what’s going on with the code—self-documenting code is always a good thing.

One compelling use of the Seam Events API is to develop a mechanism for preparing status messages that’s agnostic of UI technology, as opposed to using the Seam FacesMessages component. The event would include the message and severity, and the observer would register the message with the UI framework. Before you run off to develop this idea, I should let you know that Seam 2.1 supports creating technology-agnostic status messages.

Before moving on to observers, let’s look at how to raise events declaratively.

Using @Raiseevent

The @RaiseEvent annotation, summarized in table 6.7, offers the most convenient way to raise an event from a component, but you give up control over when the event is raised. Observers are notified of an event raised by the @RaiseEvent annotation upon successful completion of the component method on which the annotation resides.

Table 6.7. The @RaiseEvent annotation

Name:

RaiseEvent

Purpose:

Raises a component event, which is passed on to registered observers upon successful completion of the component method

Target:

METHOD

Attribute

Type

Function

value String[] One or more event names to be raised. Default: the method name.

If you recall, successful completion means either the method is void or it returns a non-null value and no exceptions were thrown. The events are fired after successful completion of the method because, like bijection, it’s implemented as an interceptor. This interceptor wraps the transaction interceptor, so if your transaction is configured to complete (commit or roll back) after successful completion of the method, the event will be raised after the transaction completes—though you still don’t have the granularity of distinguishing between a commit and a rollback.

With that, let’s change the previous example so that the raising of the golferRegistered event is declared as metadata:

  @RaiseEvent("golferRegistered")
  public String register() {
      ...
      entityManager.persist(newGolfer);
      ...
      return "success";
  }

The @RaiseEvent annotation can be used to raise an arbitrary number of events. However, it can’t be used to pass arguments to the observer (at the time of this writing). Thus, @RaiseEvent is good when you just want to notify observers that something has happened. If information is critical to understanding the event, you’ll likely want to use the Events API.

With all of these events floating around out there, you must learn how to observe them.

6.5.2. Defining an event @Observer

Events are observed by component methods or EL method expressions that you have registered as observers. An observer can be registered using the @Observer annotation or by attaching an event to an EL method expression in the component descriptor. Let’s explore the @Observer annotation first.

The @Observer annotation, summarized in table 6.8, can be added to a method of any Seam component. It can observe one or more events by name, specified in the value attribute. By supplying the create attribute, you can also specify whether to have the observer component created if it doesn’t exist at the time the event is raised. This flag is equivalent to the autocreate functionality on components.

Table 6.8. The @Observer annotation

Name:

Observer

Purpose:

Registers a component method to observe events by name. The method can accept arguments if the producer passes arguments along with the event.

Target:

METHOD

Attribute

Type

Function

value String[] One or more event names to be observed. Default: none.
create boolean Controls whether the observer component is created if it doesn’t exist at the time the event is raised. Default: true.

Although it’s possible to capture multiple events in the same method, you have to remember that an event can pass arguments, thus dictating the signature of the observer. You can’t use the same method to observe multiple events if the type and amount of arguments passed by the events vary. You would be much better off refactoring the observation logic into multiple methods in this case.

Let’s observe the golferRegistered event and record it for the marketing team’s statistics. Instead of going through the exercise of setting up an entity and database table, we just throw in some logging statements:

  @Name("registrationBookkeeper")
  @Scope(ScopeType.APPLICATION)
  public class RegistrationBookKeeper {
      @Logger private Log log;
      private int cnt = 0;

      @Observer("golferRegistered")
      synchronized public void record(Golfer golfer) {
          cnt++;
          log.info("Golfer registered – username: " + golfer.getUsername());
          log.info(cnt + " golfers have registered since the last restart");
     }
  }

Since the record() method is a component method, it will trigger bijection, which can be useful for injecting the EntityManager when you build the real implementation. You can also register the observer using the component descriptor. The latter is useful if you can’t add the @Observer annotation to the class—perhaps because it can’t be modified—or to have non-Seam components observe events. To do so, you’d add the following declaration in the component descriptor:

  <event type="golferRegistered">
    <action execute="#{registrationBookkeeper.record(newGolfer)}"/>
  </event>

You can specify any number of actions to be executed for a single event. Notice that in this case, we’re taking advantage of the JBoss EL to pass an argument to the method expression. At the time the event is raised, the newGolfer context variable is still in scope, so it’s possible to reference it for the purpose of passing it to the observer.

Let’s look at another way that events are triggered: tying them to page events.

6.5.3. Raising events on page transitions

Events can be declared in the page descriptor, allowing them to be tied to any page-related activity such as a page render or navigation transition. The <raise-event> element can be nested in any of these nodes: <page>, <navigation>, or <rule>. As with the @RaiseEvent annotation, it’s not possible to pass parameters using this approach.

Let’s implement the marketing department’s request to capture cases when the registration form is canceled. To do this, we trigger an event on a navigation rule:

  <page view-id="/register.xhtml">
    <navigation>
      <rule if-outcome="cancel">
        <raise-event type="registrationCanceled"/>
        <redirect view-id="/home.xhtml"/>
      </rule>
    </navigation>
  </page>

You’ll also want to ensure that the cancel button on the registration page, /register.xhtml, is changed to send the cancel outcome:

  <h:commandLink value="Cancel" action="cancel" immediate="true"/>

Events are also useful for notifying observers when a page is going to be rendered.

Page events in place of page actions

In Chapter 3, you used a page action to preload data before rendering the page. Since the goal is to observe and perform logic for the render event, it would be more appropriate—and self-documenting—to raise an event and have a component method observe it. Let’s use this approach to preload the list of facilities on the facility directory page:

  <page view-id="/FacilityList.xhtml">
    <raise-event type="facilityList.preRender"/>
    ...
  </page>

Next, the @Observer annotation is applied to the preloadFacilities() method to watch for this event:

  @Observer("facilityList.preRender")
  public void preloadFacilities() {
      getResultList();
  }

Even before you start producing your own events, you have a whole slew of built-in events that are raised by the Seam container. You may find that your first steps with events will be observing Seam’s built-in events.

6.5.4. Built-in events

Events are one of the richest parts of Seam. If you had a nickel for every event that Seam raised, you’d surely be a rich developer. Listing every event that Seam raises would eat a gratuitous amount of space. I encourage you to consult the Seam reference documentation for a comprehensive list. Here’s a list of some of the events that Seam raises:

  • Seam container initialized
  • Context variable assignment (added, removed)
  • Component life-cycle events (created, destroyed)
  • Scope events (created, destroyed)
  • Authentication events
  • Transaction events (before complete, commit, rollback)
  • Exception events (handled, not handled)
  • Conversation, page flow, business process, and task boundaries
  • JSF phase transitions (before, after)
  • JSF validation failed
  • User preference change (theme, time zone, locale)

In certain cases, such as when a context variable is modified, the event raised includes the name of the subject. For instance, when the context variable newGolfers is added to the page scope, the name of the events that are raised are

  • org.jboss.seam.preSetVariable.newGolfers
  • org.jboss.seam.postSetVariable.newGolfers

In some cases, the subject is also passed as an argument to the event, such as when a component instance is created. When the profileAction component is created, the event org.jboss.seam.postCreate.profileAction is raised and the profileAction instance is sent as an argument. Observing this event allows you to place your postcreate logic for a component on a different component entirely:

  @Name("profileActionHelper")
  public class ProfileActionHelper() {
      @Observer("org.jboss.seam.postCreate.profileAction")
      public void onCreate(ProfileAction profileAction) { ... }
  }

In Chapter 11, you’ll learn how events can be used to minimize the disruption when requiring users to log in by capturing the current view before sending them to the login page and returning them to the captured page after successful authentication. You’ll also use the postauthentication events to tune the HTTP session timeout as a way of managing memory.

Events are one way of separating concerns so that each component can focus on a specialized task, yet those individual tasks can be brought together using the event-observer pattern. Another way to decouple components and apply cross-cutting concerns is to use interceptors. The next section covers how interceptors are handled by both EJB 3 and Seam.

6.6. Custom method interceptors

As you’ve learned, Seam makes liberal use of method interceptors to weave functionality into components, an application of AOP. While AOP is extremely powerful, concepts such as pointcuts and advice are overly complex for many applications and can overwhelm developers. Seam and EJB 3 truly simplify AOP by making it straightforward to register cross-cutting logic, thus adding to the behavior Seam provides. Seam even aids in stereotyping the cross-cutting concern, allowing it to be applied declaratively. In this section, you’ll learn about Seam’s interceptor support and how it correlates with EJB 3 interceptors.

6.6.1. Two sides to the interceptor coin

Both Seam and EJB allow you to intercept calls to methods of a component by registering one or more interceptors on the component. Unlike other EJB features that Seam brings to a non-Java EE environment, Seam’s interceptor support is not just a clone of what EJB offers. Seam introduces client-side interception, stateless interceptors, and stereotypes. While only Seam’s interceptors can be applied to a JavaBean component, its interceptors complement the EJB 3–style interceptors on Seam session bean components. Let’s take a quick look at EJB 3 interceptors, and then contrast them with the interceptor support that Seam provides.

EJB 3 interceptors

The only requirement for defining an EJB interceptor is that the class have a method with the following signature:

  @AroundInvoke
  public Object methodName(InvocationContext ctx) throws Exception { ... }

The name of the method is arbitrary. The InvocationContext provides access to information about the intercepted method and component. Within the method, you can invoke the intercepted method using the following call:

  ctx.proceed();

That’s pretty much all there is to an interceptor method. The @AroundInvoke method intercepts all business methods on the component to which it’s applied. You can intercept life-cycle methods of the component by defining additional methods on the interceptor class annotated with the Java EE life-cycle annotations, described in section 4.5.5 in Chapter 4. The life-cycle interceptor methods use the same signature as the @AroundInvoke method.

Interceptors are applied in an EJB component in one of three ways:

  • The EJB component is its own interceptor.
  • The interceptor is applied at the class level.
  • The interceptor is applied at the method level.

The first option is probably the easiest to understand. The @AroundInvoke method is placed on the EJB component that it intercepts. In this case, the component is intercepting itself. You typically only use this solution while prototyping an interceptor, because it provides little in the way of separation of concerns.

The other two options are likely what you will use long term. In this case, the interceptor class is registered on the component using the @Interceptors annotation, which accepts a list of interceptor classes. This annotation can be applied at the class or method level. When applied at the class level, every business method is intercepted by the @AroundInvoke method on the interceptor class, and the life-cycle methods are intercepted by the corresponding life-cycle method on the interceptor:

  @Interceptors(RegistrationInterceptor.class)
  public class RegisterActionBean implements RegisterAction { ... }

When applied at the method level, the @AroundInvoke method on the interceptor is only applied to that particular method:

  public class RegisterActionBean implements RegisterAction {
      @Interceptors(RegistrationInterceptor.class)
      public String register() { ... }
  }

In either case, the interceptor is external to the component and provides good separation of concerns. Note that the interceptor class doesn’t have to be an EJB component itself. As I mentioned, EJB interceptors are pretty straightforward. Let’s take a look at how Seam interceptors differ.

Seam interceptors

Seam interceptors differ from EJB interceptors in several ways. The first is cosmetic. Seam provides synonyms for the @AroundInvoke annotation and the InvocationContext that can be used when the EJB API isn’t available. The synonyms also work in a Java EE environment, so it makes no difference which ones you choose. To intercept life-cycle methods, though, you must use the Java EE life-cycle annotations. Aside from cosmetic differences, a Seam interceptor class appears the same as an EJB interceptor class.

With that out of the way, we can get into the interesting differences:

  • Seam interceptors can be stateful or stateless.
  • Seam interceptors can be applied on the client or the server.
  • Seam interceptors are applied to a component as a stereotype.

The fact that Seam interceptors can be stateful or stateless addresses one of the main shortcomings of EJB interceptors and emphasizes Seam’s commitment to stateful components. If the interceptor is stateful, it’s bound to the lifetime of the component to which it’s applied. Otherwise, Seam registers a singleton instance of the interceptor. The second difference is made possible by the fact that a Seam session bean component is a double proxy. Seam proxies the EJB instance and intercepts method calls before proceeding with the method on the session bean. Then, on the server, EJB applies its set of interceptors before proceeding with the business method. Seam is able to apply its interceptor on either side of the fence because Seam registers itself as an EJB interceptor. The final difference deals with how Seam interceptors are registered. Before we get to stereotypes, let’s see how Seam is able to customize the behavior of an interceptor.

6.6.2. Defining a Seam interceptor

I mentioned earlier that, aside from the synonym types, the signature of a Seam interceptor class looks no different than an EJB interceptor class. If that’s the case, Seam makes the interceptor stateful and invokes it on the client side. If you want to tune either of these two properties, you need to add the @Interceptor annotation to the interceptor class.

Customizing your interceptor

The @Interceptor annotation supports two properties, stateless and type, which control whether the interceptor is stateless or stateful and whether it’s invoked on the client or the server. If the interceptor is applied to a JavaBean component, the value of the type attribute isn’t relevant. Here’s an example of a stateless, client-side interceptor:

  @Interceptor(stateless = true, type = InterceptorType.CLIENT)
  public class StatelessClientSideInterceptor {
      @AroundInvoke public Object aroundInvoke(InvocationContext ctx) {
          ctx.proceed();
      }
  }

One of the main benefits of using a Seam interceptor is to make it stateful, so I encourage you to take advantage of this feature. This allows you to safely store data in the interceptor between method calls just as you would the stateful component it’s intercepting.

The interceptor type you choose depends on the call stack in which you want your interceptor to execute. If you want to short-circuit the call to the EJB component or wrap the work performed in the EJB container, you need to use a client-side interceptor. For instance, you can retry a call to an EJB component (or other remote service) in the event of a failure. On the other hand, if you want the interceptor to execute within the EJB interceptor call stack—duking it out with the other interceptors installed on the EJB session bean—a server-side interceptor is appropriate. The server-side interceptor is sufficient if you’re simply adding behavior to the method invocation.

Having decided how to author your interceptor, you need to learn how it’s applied to a Seam component. Paying attention here is important because the way Seam interceptors are registered with a Seam component differs from how EJB interceptors are registered with an EJB component. While Seam provides a synonym annotation for @Interceptors, you do not apply it directly to the component class as when you’re registering EJB interceptors.

Interceptors as stereotypes

The @Interceptors annotation is a meta-annotation, meaning it must be added to an annotation, not directly to a class. That annotation is then declared on the component class and the interceptor from the annotation is carried through to the component. Building on the earlier example, we first define an annotation to host the RegistrationInterceptor:

  @Target(TYPE)
  @Retention(RUNTIME)
  @Interceptors(RegistrationInterceptor.class)
  public @interface RegistrationAuditor {}

Next, the annotation is added to RegisterAction JavaBean component:

  @Name("registerAction")
  @RegistrationAuditor
  public class RegisterAction { ... }

There are two reasons for this level of indirection. First, it’s how Seam is able to isolate interceptors so that they can be applied on the client side rather than handled by the EJB container on the server side. If they were registered directly with the class, the EJB container would try to gain control. But there’s a more compelling reason, which is design oriented.

Annotations that describe the semantics of a class are known as stereotypes. If you’re familiar with UML, the concept is much the same. With the EJB 3 model, the @Interceptors annotation placed directly on the class doesn’t tell you much about why it is there and what the interceptors do. In Seam, the interceptors are added to a custom annotation. When this annotation is added to the component class, it describes the behavior that it brings without exposing the mechanism by which that behavior is applied. You see this pattern used heavily in JSR 299 (Web Beans).

Just as events and interceptors offer a way to loosely couple sequential logic and apply cross-cutting logic, factory and manager components offer a way to loosely couple the creation of context variables. Let’s explore how these special components help to build on Seam’s inversion of control principles.

6.7. Factory and manager components

Sometimes, there’s more to a context variable than first meets the eye. Up to this point, you’ve learned that requesting a context variable from the Seam container may spawn an instance of a component with the same name. After the instance is resolved, you still have to access its properties or methods to get to the actual data. Factory and manager components allow you to use context variables to produce the underlying data, rather than just a component instance. When the context variable is requested, a data provider is invoked—typically a method on a Seam component—and the return value is bound to the context variable. Thus, the value of a context variable bound to factory or manager components is said to be calculated. A factory component is a generalization of a @Factory method, and a manager component is any component that has one, and only one, @Unwrap method.

What makes factory and manager components interesting is that they act just like regular components; they use bijection, they can be injected into another component using @In, and they can be referenced in EL bindings. Let’s begin by exploring the factory component.

6.7.1. A context variable @Factory

The factory component is a bit of a misnomer. Technically, it’s just a data provider—a component method or EL expression—that can bind a value to a context variable when the context variable is requested. Factory components are useful for creating context variables lazily, such as from a JSF view, as opposed to being outjected from an action method.

Why factories are needed

Before we dive into factories, I want to clarify the benefit they provide over just using a JavaBean-style “getter” method on a component to retrieve data (such as the list of new golfers). What you must understand is that the EL resolver is very persistent about resolving value expressions. If you put logic in a getter method, it may get called a ridiculous number of times in one request. In a JSF view, there are a lot of places where you need to repeatedly access a calculated value to perform such tasks as conditional rendering (checking whether a collection is empty, perhaps), and the branches of the UI component tree where these expressions are located are each visited several times during the JSF life cycle. It’s just a natural part of how JSF and the EL work.

There’s no harm in using a getter method in a value expression as long as the method simply returns the internal state of an object. However, if that method performs logic, especially logic that accesses the database, this can result in a significant performance problem. A factory component allows you to calculate the value once and bind the result to a context variable. Subsequent requests for that context variable use the previously calculated value rather than running the logic again. This immediately solves the problem just cited. Always be wary of putting heavyweight logic in getter methods and instead use factories. The performance-conscious folks will be thankful to you.

That explanation should get you excited about learning more about factories. Let’s see what types of data providers can serve as factories and then dive into the process.

Factory data providers

As we discussed earlier, when a context variable is requested, Seam tries to locate it in one of the available contexts. When that search turns up empty, and the lookup is operating with the create option, Seam turns to initializing a value. I’ve held back some of the details of how this value is produced. Before looking for a matching component definition to instantiate an instance, Seam first seeks out a volunteer to produce a value. This volunteer is known as a factory. A factory can be implemented using one of the following providers:

  • A Seam component method, marked with the @Factory annotation
  • An EL value or method expression, configured in the component XML descriptor

The reason these delegates are called factories is because they “manufacture” values for the context variables they represent. When a factory is resolved, the factory provider is invoked and the return value is bound to the factory name in the designated scope. If the factory produces a null value, Seam moves on to the next step in the lookup process.

Here’s an example of a value expression factory defined in the component descriptor:

  <factory name="course" value="#{courseHome.instance}"/>

When the context variable course is requested, this value expression is resolved and the result is bound to the corresponding variable name in the event scope (the event scope is the default scope for <factory>). Similarly, the factory product can be resolved from a method expression. Here, the findNewGolfers() method populates the newGolfers context variable, later defined as an annotation-based factory:

  <factory name="newGolfers" method="#{profileAction.findNewGolfers}"/>

A factory data provider supports autocreate functionality just like a component, allowing it to be resolved even when the corresponding context variable is requested in lookup only mode. Unlike a component, factories can create any value, not just component instances. In fact, the result of a factory can be injected into the property of a component using @In:

  @In(create = true) Course course;

In this case, course may be the name of a component or the name of a factory. The injection point doesn’t know the difference. It just depends on what’s available. Factories support autocreate, so the create attribute on @In isn’t necessary if autocreate is enabled on the factory. There are, of course, subtle differences between a component and a factory, but they are small enough that it’s reasonable to refer to factories as components. Let’s consider what happens when a context variable associated with a factory is requested.

The factory process

The factory process is illustrated in figure 6.10. While this process can get fairly involved, it essentially follows one of three approaches to produce a context variable:

  • Return a value, which Seam then assigns to the context variable
  • Outject the context variable
  • Assign a value to the context variable in the factory method
Figure 6.10. The process that Seam follows to look up a context variable backed by a factory component

To start, you probably just want to have the factory method return a value. You may find, however, that you want to cache the factory result in a property. In that case, you make the factory method return void and outject the property in which the result is stored. Outjection is also useful if you want to wrap the result in a @DataModel. The final option highlights the fact that the factory process ensures that the context variable is still null before assigning a value to it using either of the first two strategies.

Whew! There sure is a lot going on in the factory process. Don’t let it overwhelm you, though. There just happens to be many different ways to use a factory, which is why the diagram has so many steps. The best strategy is to pick the variation that works best for you and stick with it.

If you only take away three points about a factory, make them these:

  • A factory provider is invoked when the context variable it represents is accessed and the context variable is missing or its value is null.
  • A factory can either assign the context variable itself (explicitly or as a result of outjection) or return a value that’s to be assigned to the context variable.
  • A factory can trigger bijection if the factory provider is a component method (and bijection is not disabled on that method).

One of the side effects of a factory whose data provider is a component method is that it triggers bijection. It’s convenient to have access to injected values within the method, but where things get interesting is during outjection. As stated earlier, if the factory method has a void return value, Seam relies on outjection to populate the context variable that the factory represents. However, additional properties may also be outjected at the same time. So when a factory is resolved, other context variables may just suddenly appear.

Once you get the factory process under your belt, you’ll find yourself using factories all the time. Let’s see how a factory can be used to prepare the list of new golfers on demand.

Initializing context variables on demand

Earlier, I showed you how to expose the collection of the newest golfers in the form of a JSF DataModel using a page action. Page actions are useful in small quantities. Once the page starts to get reasonably complex, the number of page actions can balloon out of proportion if used to prepare context variables needed in the page. A better solution is to allow each variable to be initialized on demand by using a factory.

One way to define a factory is by placing the @Factory annotation, summarized in table 6.9, above a component method. This method is called whenever the context variable, whose name is provided in the value attribute of the annotation, needs to be initialized.

Table 6.9. The @Factory annotation

Name:

Factory

Purpose:

Marks a method as a factory for a context variable. Used to supply a value when an uninitialized context variable is requested.

Target:

METHOD

Attribute

Type

Function

value String The name of the context variable that this factory method initializes. Default: Bean property name of method.
scope ScopeType The scope where the context variable is set. The scope should not be used when the factory method is void. Default: inherited from the host component, or the event scope if the component is stateless.
autoCreate boolean Indicates that this factory should initialize the context variable, even when requested in lookup-only mode. Default: false.

Let’s designate the findNewGolfers() method as a factory rather than as a page action. The @Factory annotation above the findNewGolfers() method in listing 6.4 declares this method as the factory for the newGolfers context variable. Seam invokes this method whenever the context variable is null or missing. However, since the method’s return type is void, Seam expects the context variable to be populated as a result of outjection.

Listing 6.4. A factory component that produces a list of new golfers
  @Name("profileAction")
  public class ProfileAction {
      protected int newGolferPoolSize = 25;
      protected int newGolferDisplaySize = 5;

      @RequestParameter protected Long golferId;

      @In protected EntityManager entityManager;    

      @DataModelSelection
      @Out(required = false)

             protected Golfer selectedGolfer;

      @DataModel(scope = ScopeType.PAGE)     
      protected List<Golfer> newGolfers;

      public String view() {
          return "/profile.xhtml";
      }

      @Factory("newGolfers")      
      public void findNewGolfers() {
          newGolfers = entityManager.createQuery(
            "select g from Golfer g order by g.dateJoined desc")   
            .setMaxResults(newGolferPoolSize)
            .getResultList();

          Random rnd = new Random(System.currentTimeMillis());
          while (newGolfers.size() > newGolferDisplaySize) {     
              newGolfers.remove(rnd.nextInt(newGolfers.size()));
          }
      }
   }

The first time the #{newGolfers} value expression is encountered in the JSF view, the newGolfers context variable is uninitialized. Seam will turn to the @Factory method to resolve a value. Before the method proceeds, the EntityManager property is injected. The method then uses the EntityManager to query the database for the newest golfers , shuffles the result, and pares it down to the configured display size . Outjection is then applied, which initializes the newGolfers context variable as a JSF DataModel . Seam then returns control to the view renderer, which uses the newly formed data model to render the <rich:dataList>.

Here are some rules that you should be aware of when using a factory method:

  • If a scope isn’t specified for a factory method that returns a value, the scope of the host component is used, or the event if the host component is stateless.
  • A factory method with a defined scope can’t allow a value with the same name to be outjected as a result of the call.
  • If a factory method doesn’t specify a scope, and both a value is outjected and the factory returns a value, the outjected value takes precedence over the value returned by the factory method.

The @Factory annotation on a component method is the only way to designate a factory in Java code. To use a method- or value-binding expression, you must declare the factory in the component descriptor using <factory>. As a quick reminder:

  <factory name="newGolfers" method="#{profileAction.findNewGolfers}"/>

But a more common use of <factory> is for creating component aliases.

Factories as aliases

Factories that are scoped to the stateless context and are mapped to an EL value expression are referred to as aliases. Earlier, you saw an alias that resolves to the getInstance() method on the CourseHome component, defined here to be stateless:

  <factory name="course" value="#{courseHome.instance}" scope="stateless"/>

Since the stateless context doesn’t retain the value after lookup, the alias is resolved each time the factory name is requested, making it a shortcut for a more complex expression.

 

Tip

I prefer to scope aliases to a real context, such as the event scope, to avoid unnecessary, redundant lookups. As long as the target value won’t change during the lifetime of the context in which the alias is stored, it’s a safe bet.

 

Another typical use of an alias is to provide a terse name for a fully qualified context variable name. All of the built-in Seam components use namespaces, which make their names very long. You can abbreviate a fully qualified component name using an alias, as I’ve done here for the built-in FacesMessages component:

  <factory name="facesMessages"
    value="#{org.jboss.seam.faces.facesMessages}"
    scope="stateless" auto-create="true"/>

But guess what? Most of the built-in components, such as this one, can already be referenced by their unqualified names, making this factory unnecessary. Seam sets up this alias by importing the context variable prefix in the component descriptor using the <import> tag, which you learned about in the previous chapter:

  <import>org.jboss.seam.faces</import>

You can declare imports for your own component names as well. As a reminder, imports defined in the component descriptor are applied globally for a project. If you only want to import a context variable prefix in the context of a single Java class or package, you can do so using the @Import annotation, summarized in table 6.10.

Table 6.10. The @Import annotation

Name:

Import

Purpose:

Imports a context variable prefix to allow components having that prefix to be referenced using their unqualified component names

Target:

TYPE (class), PACKAGE

Attribute

Type

Function

value String[] A list of context variable prefixes that are used to qualify component names after trying with the unqualified name. Default: none (required).

Let’s assume the context variable name of the GolferValidator component is org.open18.validation.golferValidator. We can import its namespace prefix into the RegisterAction component using @Import:

  @Name("registerAction")
  @Import("org.open18.validation")
  public class RegisterAction {
      ...
      @In protected GolferValidator golferValidator;
      ...
  }

We can also import it for all components in the org.open18.action package by applying the @Import annotation to the package-info.java file in that package:

  @Import("org.open18.validation")
  package org.open18.action;
  import org.jboss.seam.annotation.Import;

Note that just as in the Java package system, it’s possible to refer to a context variable in the same namespace as the current component by referring to that variable using its unqualified name. If you named the RegisterAction component org.open18.action.registerAction, you could refer to it from another component whose name is prefixed with org.open18.action using the name registerAction.

So you can see that factories are a powerful and flexible feature of Seam. They help to close gaps when data is needed in an arbitrary location, such as a component buried deep inside a JSF view or as an injected dependency that needs to perform prerequisite work. Factories allow context variables to be dynamic, boasting more meaning than what is observed at first glance. Another type of component that helps make context variables dynamic is the manager component.

6.7.2. Components that @Unwrap

The @Unwrap annotation acts like a magician’s handkerchief. You see the handkerchief go into the hand, but when the hand opens, you get something entirely different. This trick is called a manager component. A manager component has exactly one method that is marked with the @Unwrap annotation. When a manager component’s name is requested from the Seam container, an instance of the component is located or created, and then “unwrapped” before it’s returned to the caller, thus beginning the handkerchief trick. During the unwrap stage, the @Unwrap method is executed and the return value of the method is passed on to the requester in place of the component itself. Indeed, the manager component is a slippery little devil. A summary of the @Unwrap annotation is shown in table 6.11.

Table 6.11. The @Unwrap annotation
Name: Unwrap
Purpose: Identifies the method whose return value is used in place of the component instance itself when the component name is resolved
Target: METHOD

The @Unwrap method differs from the @Factory method in that it is stateless, meaning it’s invoked every time the host component’s name is accessed. Because it’s stateless, the return value of the creation method isn’t bound to the context variable, as with a factory. Instead, the context variable stores the instance of the manager component, which is the only stateful part. To get to the actual value, the @Unwrap method must be called. Obviously, you have to be careful about what you put into the @Unwrap method since it’s going to end up being called many times.

The manager component is more powerful than the factory component for these reasons:

  • It’s a real component (the factory is only a pseudocomponent).
  • Its properties can be configured just like any other component.
  • It can have life-cycle methods (@Create, @Destroy).
  • The @Unwrap method is called every time the component name is accessed.
  • It can hold internal state.
  • It can observe events that can trigger updates to its state.

As its name suggests, a manager component is good at managing data. The data stands in for the component when the component is accessed or injected using @In, but otherwise it behaves just like any other component. You can think of a manager component as a sophisticated alias, where the @Unwrap method is responsible for looking up the value of the alias. What makes the manager component compelling is that it can alias an object that isn’t accessible via a value- or method-binding expression. One such example from the Seam API is the manager component that retrieves the JSF FacesContext instance, a static method on the FacesContext class:

  @Name("org.jboss.seam.faces.facesContext")
  @Scope(ScopeType.APPLICATION)
  public class FacesContext {
     @Unwrap public javax.faces.context.FacesContext getContext() {
        return javax.faces.context.FacesContext.getCurrentInstance();
     }
  }

Another example from the Seam API is a manager component that retrieves the current date:

  @Name("org.jboss.seam.framework.currentDate")
  @Scope(ScopeType.STATELESS)
  public class CurrentDate {
     @Unwrap public Date getCurrentDate() {
        return new java.sql.Date(System.currentTimeMillis());
     }
  }

The manager component offers a convenient way to make Java constants, enum values, and other static method return values accessible via the EL. Manager components are also good for loading resources such as a Hibernate SessionFactory, a Java mail session, or a JMS connection. In fact, Seam uses manager components for all of these resources. It would be possible to create regular components for these resources and then register an alias that returns the value of the method that retrieves the runtime configuration, but the @Unwrap method saves you the extra step. If you’ve ever used one of Spring’s factory beans, a manager component is the same idea.

Let’s put the manager component into practice by using it to maintain the list of new golfers shown on the home page. You see, we’re hoping that Open 18 is going to be a big success and as a result, a lot of people are going to be hitting the home page. We don’t want every single hit to result in a database query or it could hurt performance. While we could certainly get a faster database server, it’s prudent to take the burden off the database, especially since we’re repeatedly asking it the same question (who are the new golfers?).

Listing 6.5 shows a manager component that maintains a cache of the new golfers in the application scope. Any time a new golfer registers, it observes the golferRegistered event and refreshes the cache.

Listing 6.5. A component that manages the collection of new golfers
  package org.open18.action;

  import org.jboss.seam.ScopeType;
  import org.jboss.seam.annotations.*;
  import org.open18.model.Golfer;
  import javax.persistence.EntityManager;
  import java.util.*;

  @Name("newGolfersList")
  @Scope(ScopeType.APPLICATION)
  public class NewGolfersList {
      private int poolSize = 25;
      private int displaySize = 5;

      @In protected EntityManager entityManager;     

      protected List<Golfer> newGolfers;

      public void setPoolSize(int poolSize) {
          this.poolSize = poolSize;
      }

      public void setDisplaySize(int displaySize) {
        this.displaySize = displaySize;
      }

      @Create     
      public void onCreate() {
          fetchNewGolfers();
      }

      @Unwrap     

      public List<Golfer> getNewGolfers() {
          return newGolfers;
      }

      @Observer(value = "golferRegistered", create = false)     
      synchronized public void fetchNewGolfers() {
          List<Golfer> results = entityManager.createQuery(
               "select g from Golfer g order by g.dateJoined desc")
               .setMaxResults(poolSize).getResultList();

          Collections.shuffle(results);
          Random random = new Random();
          while (results.size() > displaySize) {
              results.remove(random.nextInt(results.size()));
          }
          newGolfers = results;
      }
  }

The golfers are fetched when the component is first created and whenever a new golfer registers . Manager components support bijection just like any other component. Every time the component name, newGolferList, is accessed, the @Unwrap method is called and the cached list of golfers is returned.

You may notice that this component exposes two configuration properties, poolSize and displaySize, which control how many golfers are selected from the database and how many are displayed, respectively. To change the defaults, use the following component configuration:

  <component name="newGolfersList">
    <property name="poolSize">10</property>
    <property name="displaySize">3</property>
  </component>

If we swapped out the newGolfers on the home page with the newGolfersList, we’d lose the ability to capture a selection using a command link. It isn’t possible to create clickable lists using a manager component alone (without some creative backbreaking). Manager components are designed to maintain data, not to serve as an action bean or data model selector. Therefore, we still need a factory to provide a value for the newGolfers property on ProfileAction to keep the current functionality. Instead of querying for golfers in the ProfileAction, the newGolfersList manager component will be consulted to get the list. The relevant changes to ProfileAction are shown in listing 6.6.

Listing 6.6. A factory component that uses a manager component as a data provider

The clickable list of new golfers works just as it did before. The only change is that the manager component now provides the list of new golfers rather than performing the entity query directly in the factory method. The best part about this design is that the manager now acts as a data access object (DAO), separating the data access logic from the business logic and making the components easier to test in isolation.

Factory and manager components act as just-in-time variable assignment. Your code references a context variable as if it has already been set and then one of two delegates, either the factory or the manager, creates it, just in time for you to make use of it. But what makes it even better is that the method has access to injected resources to help it create the variable.

6.8. Summary

This chapter championed Seam for delivering a much-needed technology update to inversion of control by making injections dynamic and introducing the concept of outjection—the two sides of bijection. You hardly need to be reminded now that bijection occurs every time a method on a Seam component is invoked, so not only are dependency references kept up to date, but it’s also possible to inject data from a narrow scope into a component stored in a wider scope.

You learned how you can inject components, request parameters, and value expressions into the bean properties of a Seam component. You also learned that property values can be pushed back out into a Seam context, allowing them to be accessed by other components or in a JSF template. You learned that, when pushing out collections, Seam can wrap them in a JSF DataModel to be used in a UIData component. Seam can also capture the selection from that UIData component and inject it into another property on the component on a subsequent postback.

You discovered that there’s a lot more to the component retrieval process than what appears at first glance. Often, context variable names resolve to component instances, but they can also be fed by a factory method or value expression or even resolve to the return value of a delegate method on manager components.

To accomplish the pinnacle of inversion of control, you learned that it’s possible to cut the ties between components completely while still being able to establish a linear flow of logic by allowing the Seam container to mediate component event notifications and trigger custom interceptors. Both make it easy to apply cross-cutting concerns to objects without jeopardizing their status as POJOs.

The exchange of context variables in the Seam container is very rich, yet for most uses, is confined to a couple of commonly used annotations. Injecting, outjection, and resolving of context variables is crucial for exchanging data between components and the view. In the next chapter, you’ll learn how conversation annotations and page flow definitions help to propagate state across requests. The factory component also gets additional limelight, playing a key role in launching a conversation. The promise of improved state management mentioned in Chapter 4 is given substance in the next chapter, so read on because the most exciting parts of Seam are yet to come.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.114.85