Chapter 4. Components and contexts

This chapter covers

  • Defining Seam components using annotations
  • Hooking into component life-cycle events
  • Using EJB session beans as Seam components
  • Accessing instances of Seam components

This chapter introduces the components and contexts that Seam manages. If you’ve worked with the Spring Framework, the idea of declaring managed objects should be familiar to you. In Seam, however, you replace all uses of the word bean with the word component. Like Spring, Seam boasts similar capabilities to define, configure, and instantiate components. In one regard, you can think of Seam as a lightweight container. It doesn’t force you to code to container-specific interfaces, require you to adopt a special programming model, or mandate that your components even live in a container. Instead, components are just plain old Java objects (POJOs). What makes Seam unique is that it leverages existing containers and contexts to host the objects it instantiates, so it’s more accurately classified as a meta-container. After obtaining an instance of a component, Seam decorates it with enterprise services that are applied transparently through the use of method interceptors. The main advantage that Seam has over other managed containers such as Spring is that Seam treats a component’s context with equal importance as the component itself. Thus, the focus of this chapter is not just components, but rather contextual components.

Chapter 2 provided the opportunity to get an application up and running and observe Seam in Action. I am sure those exercises, as well as references from the previous chapter, have spawned loads of questions about components. I can assure you that your questions will be addressed in this chapter. To learn about Seam components, you’re going to use top-down development to add member registration to the Open 18 application. You’ll first use seam-gen to create a new entity and the supporting view and action bean component. Hibernate then takes care of adding the corresponding table to the database when the application starts based on the information in the JPA annotations on the entity class. You then study how the view, the action bean component, and the entity interact with one another. Before doing all that, though, you must understand Seam’s very essence: the contextual container.

4.1. Seam’s contextual naming container

At its core, Seam is a container that holds names, or rather, variable names. But Seam doesn’t just let all of these variable names clump together at the bottom of the barrel. Instead, Seam divides container into compartments and disperses the variables into each one accordingly. Each compartment represents a scope, or in Seam terminology, a context. A context defines where a variable name can be found and for how long it hangs around.

To be precise, the Seam container holds context variables. A context variable can hold a reference to any type of object. But, as you’ll soon discover, the real divas of the Seam container are the components. When the application interacts with a context variable holding a reference to a component instance, lots of exciting things happen. From this point forward, when I use the term context variable, I’m referring to a variable in the Seam container that stores a value in a distinct context.

 

Note

In the text, I’ll often switch between the terms scope and context. Don’t let this confuse you; these two terms are interchangeable. Technically, the context is the bucket and the scope is the marker that identifies the bucket, but that’s really splitting hairs.

 

Before advancing with your lesson on Seam components and getting the lowdown on where they hang out, I first need to briefly introduce you to Seam’s contexts and show you what sets them apart from the traditional contexts in the Java Servlet API.

4.1.1. Seam’s context model

You have a set of contexts for storing variables for the same reason that you have multiple golf clubs in your bag. In golf, you choose a club depending on how far you want the ball to go. In a web application, you choose a context depending on how long you want a variable to stick around. You’re likely familiar with the three scopes defined in the Java Servlet API: application, session, and request. The problem with this abridged set of scopes is that they are too few. It’s like trying to play a round of golf with a driver, a five-iron, and a putter. You can make it work, but there are times when you’re going to have to make each club do something for which it wasn’t designed.

The vast chasms that lie between the coarse-grained servlet scopes have claimed the lives of many applications. In addition, each servlet scope requires that you use a different API to access its variables, letting unnecessary complexity slip into the code. The Seam developers solved these two obstacles by introducing the contextual naming container, which provides a single interface to access all variables, regardless of the context in which they are stored, and introduces several new contexts that fill in the gaps between the existing ones.

4.1.2. Unifying the Java servlet contexts

Seam delivers a much needed technology update to the Java web environment by establishing a unified context model. Seam takes all contexts under the wings of its container, allowing the existing contexts to fit naturally with the new set of contexts that it contributes. By controlling the contexts, Seam provides one-stop shopping for context variables and adds useful enhancements to the existing servlet contexts.

The list of contexts Seam adds to the existing options are the stateless context, the page context, the conversation context, and the business process context. The complete set of contexts Seam supports are represented by the names in the Java 5 enum ScopeType, which you’ll see used in a couple of Seam annotations later in the chapter. Table 4.1 identifies these contexts, the associated name in the enum type, and a brief description of the context’s life span. Note that the stateless and unspecified scopes aren’t real contexts, but rather directives that instruct Seam how to handle a variable lookup.

Table 4.1. Seam’s contexts, ordered from the shortest to the longest lifespan

Context name

Enum name in org.jboss.seam.ScopeType

Description

Stateless STATELESS A nonstoring context. Forces a component to be instantiated each time the component name is resolved. Equivalent to Spring’s prototype scope.
Event EVENT Analogous to the servlet request scope. Exists from the beginning of the Restore View phase until the end of the Render Response phase in the JSF life cycle or until a redirect occurs.
Page PAGE Begins at the start of the JSF Render Response phase and carries on with each adjoining JSF postback until a redirect or a navigation to another page occurs. The storage mechanism for this context is the JSF component tree.
Conversation CONVERSATION Lasts from at least the Restore View phase until the end of the Render Response phase, even in the event of a redirect. If converted to a long-running conversation, it spans multiple requests for a single user until terminated. Conversations are propagated by the use of a special request parameter for non-JSF postback requests and through the JSF component tree on a JSF postback.
Session SESSION Analogous to the servlet session scope. Access to session-scoped component instances are serialized.
Application APPLICATION Analogous to the servlet application scope.
Business process BUSINESS_PROCESS Spans multiple conversations for multiple users as controlled declaratively by start and end states in the business process definition file.
Unspecified UNSPECIFIED A directive to indicate that the scope should be implied. Depending on the circumstance, it tells Seam to either use the scope of the current component or to performing a hierarchical search across all scopes.

Let’s briefly explore the storing contexts and the relevance of each, starting with the stateful contexts contributed by Seam.

4.1.3. Seam’s new stateful contexts

Seam makes a big deal about providing stateful contexts. As the user interacts with the application, state is accumulated and that state needs to be tracked. In traditional web applications, long-term state would be stored in the HTTP session, the de facto stateful context. However, Seam encourages you to store long-term state in a context whose lifetime aligns better with a user’s interaction. In support of this recommendation, Seam’s stateful context stack includes two new contexts, conversation and business process, that model a use case, rather than being fixed to predetermined boundaries like the HTTP session scope. Seam also exposes JSF’s view root attributes as the page context, solidifying them as a legitimate stateful context. Having these new stateful contexts is important because they help reduce load on the server while also staving off bugs caused by inadvertent sharing of state. But what’s most important about Seam’s array of stateful contexts is that they prevent misuse of the HTTP session. Let’s consider the purpose and duration of each context.

JSF has always supported a page scope, which is an unofficial classification of the attributes stored in the view root of the JSF UI component tree. Seam recognizes these attributes as first-class context variables and exposes them via the Seam page context. The page context is capable of propagating data from the Render Response phase of the JSF life cycle through at least the ensuing Invoke Application phase on a postback, then on to the Render Response phase if the same view is rendered again without a redirect. This cycle continues for as many times as the same UI component tree is restored (as a result of a postback), and is only terminated by a navigation event that occurs prior to the Render Response phase. You may have used this scope in a less formal way if you have ever included the <t:saveState> component tag from the MyFaces Tomahawk component set[1] in your application. The benefit of using Seam’s page context is that you don’t tie the state logic to the view.

1http://myfaces.apache.org/tomahawk/uiSaveState.html

The conversation and business process scopes are for managing long-running processes. Their boundaries are controlled declaratively using annotations or page descriptor tags. A conversation is a drop-in replacement for most uses of the session scope. The business process is a variation on the conversation scope, but can pass state between multiple users of the application, backed by persistence storage. You’ll learn more about conversations in chapter 7 and business processes in chapter 14 (online).

4.1.4. Seam’s enhanced servlet contexts

Seam doesn’t turn its back on the traditional Java servlet contexts—it just fixes them. Seam even uses the Java Servlet API as the underlying storage mechanism for these particular contexts, though not blindly. By taking control of these scopes, Seam is able to generalize their purpose and address flaws in how they are handled by the native container.

For instance, the event context wraps the servlet request scope. This abstraction generalizes a web request as an event so that the Java Servlet API is abstracted from Seam’s core. This generalization opens the door for Seam to support the event construct as defined in alternate environments. For typical web development, the event context and request scope are one and the same.

There are times when variables need to be retained throughout a logical request—defined as the time between when a page is requested and when it is rendered. A logical request differs from a servlet request when it involves one or more interim redirects. An example is the Redirect-After-Post pattern.[2] Unfortunately, the request scope is useless in this case since it doesn’t survive a redirect. Developers who have used the Redirect-After-Post pattern on a JSF postback know that it causes all request-scoped data prepared in the Invoke Application phase to be dropped. The data that is most often missed is the JSF status messages. So what does Seam do to help? In the absence of a long-running conversation, which you’ll learn about in chapter 7, Seam’s conversation scope propagates context variables across a logical request—what Seam terms a temporary conversation. A temporary conversation covers the purpose of a Ruby on Rails flash hash. Seam’s conversation-scoped FacesMessages component can be used, for instance, to ensure that JSF status messages survive redirects. Problem solved.

2 Redirect-After-Post is a workaround that prevents double posts as a result of the user clicking refresh after submitting a form. An explanation can be found in the Redirect After Post article on the ServerSide.com: http://www.theserverside.com/tt/articles/article.tss?l=RedirectAfterPost.

Keeping components @Synchronized

Seam improves the session context as well by protecting session-scoped components from concurrent access. Multiple requests scheduled to be handled by the same servlet (i.e., FacesServlet) may arrive at the server at the same time. These requests run in different threads but are serviced by the same servlet instance. If the application logic executed by both requests accesses the same session-scoped variable, it may result in the object referenced by that variable being altered in conflicting ways. This scenario is said to violate thread safety. To avoid it, you’d need to add the synchronized keyword to the region of code accessing the variable. Seam addresses this long-standing pitfall in web-based applications by automatically synchronizing session-scoped variables for you, and doing so with optimal efficiency. You can apply this synchronization logic to components in other scopes by adding the @Synchronized annotation to the class definition, summarized in table 4.2. This annotation allows the timeout period of the synchronization to be tuned using the timeout attribute.

Table 4.2. The @Synchronized annotation
Name: Synchronized
Purpose: Protects a component against concurrent access
Target: TYPE (class)
Attribute Type Function
timeout long The duration of time in milliseconds that a thread should be made to wait before an IllegalStateException is thrown. Default: 1000.

The important point to remember about the contextual container is that it provides access to all context variables through a consistent interface, regardless of the underlying storage mechanism. You’ll learn how to use the context API in section 4.7. With contexts covered, let’s turn the focus of our discussion to the components associated with them.

4.2. Sorting out components

The term component has been used to mean many things. In my attempts to describe it to you, I found it difficult to locate a universal definition, likely because one doesn’t exist. In theory, a component is supposedly a module that can be plugged into an application in the same way that one Lego piece is attached to another Lego piece to form a larger structure. As a person who makes a living developing software, I’m sure you’ll agree that software components are a bit more complicated than Legos.

Definitions and intentions don’t matter anyway. What matters is what the word means to you as a software developer. Up to now, we’ve assumed that a component is equivalent to a JSF managed bean. Although a Seam component can stand in for a JSF managed bean, the definition of a component is broader. A component is a set of instructions, stored in a container, that is used to create objects whose life cycle is managed by the container. After taking a deeper, but brief dive into this somewhat abstract term, I promise that this component jargon will make sense. It’s all in the naming.

4.2.1. Components vs. component instances

A component is a set of instructions, or a blueprint, for creating an object. It supplements the Java class definition. Each component is assigned a name, which is used to address the component. Table 4.3 lists several containers and how the components they manage are declared.

Table 4.3. A sampling of component containers and how components are defined in each one

Container

How classes become components

Seam Annotated with @Name or declared in components.xml
EJB Annotated with @Stateful, @Stateless, @MessageDriven or declared in ejb-jar.xml (annotations only relevant for EJB 3)
JSF Declared as managed beans in faces-config.xml
Spring Declared as a Spring bean in applicationContext.xml
Servlet container Servlets, filters, and listeners declared in web.xml

When a class becomes a component, it gains access to whatever services the container has to provide. For instance, methods on EJB session beans are automatically wrapped in transactions; servlet components and JSF managed beans have access to web-tier resource injections; Spring beans are injected with other Spring beans when instantiated. As you can see, being a component gives a class special privileges.

Great, so now you know what a component is. But since this book is about Seam, let’s focus on Seam components. A Seam component holds

  • Metadata pertaining to the creation of an instance
  • Life-cycle methods
  • Initial property values or object references

Seam creates component instances from the component definition, as figure 4.1 illustrates. When your application interacts with a component, what it’s really invoking is an instance of that component.

Figure 4.1. Component instances are created from components by the Seam container

 

Component vs. component instance: making the distinction

The relationship between a component and a component instance parallels the relationship between a Java class and a Java object, respectively. Seam even uses the Component type to describe a component definition in the same way that Java uses the Class type to describe a class definition.

 

Once an instance of a component is created, it’s stored as an attribute in its designated context under the name of the component, forming what is known as a context variable. An instance of a component is just a Java object, with one exception. It is strung with interceptors that allow Seam to keep tabs on it and manage its life cycle. Once in control, Seam is able to transparently weave behavior into the object when it is invoked. You may recognize this technique as Aspect-Oriented Programming (AOP). The idea of AOP is to handle cross-cutting concerns that would otherwise appear as boilerplate code or tie the code to a particular environment. With AOP at work, a method call isn’t just a method call. More goes on around each invocation, and that means you get more for less work.

Seam determines how to handle the object based on the instructions provided in annotations. The behavior that Seam applies includes injecting dependencies, managing transactions, enforcing security constraints, invoking life-cycle methods, and handling events triggered by the component, to mention a few. That should sound similar to how EJB works as it inspired this design.

4.2.2. Seam manages components

There’s another important characteristic of a component: a component is managed by the Seam container. The container hands out an instance of a component when the name assigned to the component is requested, as shown in figure 4.2. When this request comes in, Seam first tries to locate an existing instance. If one can’t be found, Seam will create one (if asked to do so). The instance is then returned to the requester.

Figure 4.2. Sequence diagram of a component instance being requested from the Seam container

With Seam in control, you no longer have to create instances by instantiating the Java class explicitly using the Java new operator. That isn’t to say that you can’t—but to get all of the enhancements that Seam applies to the object via AOP (which happens during the newInstance() routine in figure 4.2), you must allow Seam to create the instance for you. In that regard, the Seam container is a factory for component instances, which uses the component definitions as the schematics for how to create those instances.

The translation from component to component instance happens more often in a Seam application than it does in other lightweight containers such as Spring. That’s because context is so important in Seam. In Seam, component instances come and go along with the life cycle of the contexts in which they are stored. As you learned earlier, Seam’s contexts have varying life spans (one with no life span at all). More often than not, components in Seam are associated with stateful contexts, which means they don’t invariably hang around for the lifetime of the application.

Instance creation takes place in the Spring container just as it does in Seam, but you typically don’t give it much thought. That’s because Spring primarily uses singleton beans, whose lifetime is tied to that of the application. What’s so interesting about Seam is that it’s perfectly natural to create an object and inject dependencies into it at an arbitrary point in time, rather than when the application starts.

 

Note

Spring does provide prototype beans that are created each time they’re referenced, but they are arguably more difficult to use than Seam’s contextual components.

 

We haven’t yet addressed how Seam components are defined. To be more concise, how do the components get into the Seam container? Read on to find out.

4.3. Defining components using annotations

In Seam, you can define components in one of two ways: you can use either annotations or XML. The goal of Seam is to reduce as much XML coding as possible. Therefore, annotations are the preferred mechanism for defining a Seam component. Two common alternatives are the XML-based Seam component descriptor, covered in chapter 5, and Seam’s pluggable container mechanism—the integration that allows Spring beans to serve as Seam components—which is explored in chapter 15 (online). This chapter focuses on the annotation approach. The annotations that dictate how a component is defined are listed in table 4.4. As the chapter develops, I’ll introduce you to each one in detail.

Table 4.4. Seam annotations that define components and declare how they are instantiated

Annotation

What it does

@Name Declares a class as a Seam component and assigns it a name. When the component name is requested, Seam instantiates a new instance of the class and binds it to the context variable with the same name.
@Scope Specifies the default scope in which the context variable is stored when an instance of the component is instantiated.
@Role (@Roles) Defines alternate name and scope pairings for the component that can be usedto instantiate parallel instances of the same component for different purposes. Multiple @Role declarations are wrapped in a @Roles annotation.
@Startup Instructs Seam to instantiate the component automatically when its assigned context starts (only applies to application-scoped and session-scoped components).
@Namespace Binds a URI to a Java package and used to define custom XML namespaces in the Seam component descriptor.
@Install Used to make installation of a component conditional or to provide a precedence to override another component definition. Conditions include the presence of a class on the classpath, the presence of another component, or the debug mode setting.
@AutoCreate Tells Seam to create a new instance of the component when the component name is first requested, even if the calling code doesn’t request for it to be created.

This section concentrates on @Name and @Scope, which together form an integral component definition. The remaining annotations are auxiliary and affect how the component is processed or behaves at runtime.

4.3.1. Giving a component a @Name

It all starts with a @Name. The most fundamental way of creating a Seam component is by adding the @Name annotation to the class declaration. This annotation is summarized in table 4.5. Given that every Seam component must be assigned a name, you must provide one using the value attribute of the @Name annotation.

Table 4.5. The @Name annotation
Name: Name
Purpose: Marks a Java class as a Seam component and assigns the component a unique name
Target: TYPE (class)
Attribute Type Function
value String The name of the component. This value is used as the name of the context variable to which instances are bound in the component’s scope. Default: none (required).

You can place a @Name annotation on any class that you’d like to dress up as a Seam component. Keep in mind, though, that annotations are obviously only useful for classes that you can modify. See the accompanying sidebar describing the syntax of annotations if you’re unfamiliar with how to use them.

 

The syntax of annotations

Annotations are markers. They consist of a Java type—a name prefixed with an at sign (@)—and a set of attributes associated with that type. Annotations can be placed on interfaces, class definitions (types), methods, fields, parameters, and packages. The acceptable locations are defined by the annotation.

The attribute assignments for an annotation appear as a list of name-value pairs placed between a set of parentheses and separated by commas. There’s an exception to this syntax rule, though. If you’re defining exactly one attribute, and the name of that attribute is value, then the attribute name and the equals sign (=) can be omitted. If the name of the attribute is not value, or you’re defining multiple attributes on a single annotation, then both the attribute name and value are required for every attribute. If you’re not declaring any attributes, the parentheses can be omitted.

Attribute values can be primitives, Java types, annotations, or arrays of the former. When defining an array value, the items are placed between a set of curly braces and separated by commas. Again, there is an exception to this syntax rule. If the multivalued attribute has exactly one item, the curly braces can be omitted.

 

The coolest part of Seam is its ability to normalize the nonsemantic differences among components’ native types. The list of candidates for a Seam component includes

  • JavaBean (POJO) – JavaBean – Groovy class (Groovy Bean) – Spring bean[3]

    3 Spring beans are classes that are managed by the Spring container. Seam can “borrow” a bean from the Spring container and decorate it with Seam services, just like it can with EJB components.

  • EJB component – Stateless session bean – Stateful session bean – Message-driven bean
  • JPA entity class (treated differently than JavaBean components)

Seam decorates JavaBean components with functionality equivalent to what is provided by the EJB container, such as container-managed transaction management and security, shielding the rest of the application from being affected by the underlying type. What sets components in Seam apart from those in other containers is the attention to the context of a component instance—the scope of its existence.

4.3.2. Putting a component in @Scope

The @Name annotation is only half of the component story in Seam. The component instance has to be put somewhere once it’s created. That’s where the @Scope annotation comes in. The @Scope annotation dictates the contextual scope in which an instance of the component will be stored after it’s instantiated by the Seam container. You can, of course, put the component instance anywhere you want using a manual assignment. The @Scope annotation just determines the default scope where Seam stores the instance. Table 4.6 lists the scope that is used for each type of component if one is not specified in the component definition.

Table 4.6. Seam component classifications and default scopes

Component type

Default scope assignment

EJB stateful session bean Conversation
JPA entity class Conversation
EJB stateless session bean Stateless
EJB message driven bean Stateless
JavaBean (POJO) Event

You can override these default scope assignments by adding the @Scope annotation, summarized in table 4.7, to the class definition.

Table 4.7. The @Scope annotation
Name: Scope
Purpose: Overrides the default scope for a Seam component
Target: TYPE (class)
Attribute Type Function
value ScopeType The Seam context in which instances of this component are stored. Table 4.6 lists the default value according to component type. Default: none (required).

Let’s consider an example of how to put the @Name and @Scope annotations together to develop a new module for the Open 18 application.

4.4. A comprehensive component example

To add member registration to the Open 18 application, we first need to create an entity that holds a member’s details. Thus, we’re going to make a JPA entity class our first Seam component. Because members who register with the Open 18 application are golfers, we’ll name the corresponding entity Golfer.

4.4.1. Creating the entity components

To create the Golfer entity, navigate to the Seam distribution directory and run the seam new-entity command using the following responses:

  Entity class name: Golfer
  Master page name: golferList
  Detail page name: golfer

The new-entity command generates the Golfer JPA entity class containing a base set of properties, a page to list the golfers (golferList.xhtml) and corresponding page controller (GolferList), and a page to display the details of a golfer (golfer.xhtml) and corresponding page controller (GolferHome). The action beans components that support the CRUD operations are covered in depth in chapter 10. For now, let’s focus on using the Golfer entity class for the registration page.

The @Entity annotation added to the class declaration marks this class a JPA entity and the @Table annotation customizes the database table mapping. Whenever you add a new entity to the application, you also need to add a corresponding table to the database. Fortunately, Hibernate takes care of this task for you when the application is deployed as long as the value of the Hibernate property hibernate.hbm2ddl.auto in the resources/META-INF/persistence-dev-war.xml descriptor is update. Note that this is a change from the default value of validate set by seam-gen. Hibernate will also add additional table columns for any new entity properties that it detects.

I’ve decided to enhance the Golfer class, shown in listing 4.1, by making it a subclass of Member, shown in listing 4.2. The use of entity inheritance sets the stage for a more flexible and realistic application. However, don’t concern yourself too much with the JPA annotations, such as @PrimaryKeyJoinColumn, if they aren’t familiar to you, because the primary focus here is on using this class as a form “backing” bean in a JSF page. In order for that to happen, it needs to be declared as a Seam component.

To make Golfer a Seam component, you simply add the @Name and @Scope annotations alongside the JPA annotations, shown in bold in listing 4.1. The component name newGolfer has been chosen since the component will be called on to instantiate a fresh instance of a golfer for use in the registration form. The @Scope annotation is present to explicitly bind the component to the event scope for demonstration, overriding the default scope assignment for entity classes, which is the conversation scope. Several bean properties have been added to support the use case, which map to columns in the GOLFER table. Also note the use of the Hibernate Validator annotations which, as you learned in the previous chapter, help enforce validations in the UI.

 

Author Note

An alternative to adding @Name and @Scope to a JPA entity class is to declare the component in the Seam component descriptor using XML, which you’ll learn about in the next chapter. For now, appreciate that the use of annotations keeps things simple by eliminating XML configuration. Given that annotations are merely class metadata, they don’t affect the execution of the code (unless consulted using reflection). I confess that I prefer to limit the use of the @Name annotation to action beans and business components. Entity classes are the most frequently shared components, so conflicts can occur between teams over how to define the Seam annotations. Besides, entity classes instantiated by the persistence manager aren’t decorated with Seam interceptors. The primary use of a Seam entity component is to serve as a prototype—a new, transient (not yet persisted) instance. The prototype typically requires additional configuration that can only be defined in the component descriptor.

 

Listing 4.1. The Golfer entity class as a Seam component
  package org.open18.model;

  import java.util.Date;
  import javax.persistence.*;
  import org.hibernate.validator.*;
  import org.jboss.seam.annotations.*;
  import org.jboss.seam.ScopeType;

  @Entity
  @PrimaryKeyJoinColumn(name = "MEMBER_ID")
  @Table(name = "GOLFER")
  @Name("newGolfer")
  @Scope(ScopeType.EVENT)
  public class Golfer extends Member {
      private String firstName;
      private String lastName;
      private Gender gender;
      private Date dateJoined;
      private Date dateOfBirth;
      private String location;

      @Column(name = "last_name", nullable = false)
      @NotNull @Length(max = 40)
      public String getLastName() { return lastName; }
      public void setLastName(String lastName) {
           this.lastName = lastName;
       }

       @Column(name = "first_name", nullable = false)
       @NotNull @Length(max = 40)
       public String getFirstName() { return firstName; }
       public void setFirstName(String firstName) {
           this.firstName = firstName;
       }

      @Transient
      public String getName() { return firstName + ' ' + lastName; }

      @Enumerated(EnumType.STRING)
      public Gender getGender() { return gender; }
      public void setGender(Gender gender) {
          this.gender = gender;
       }

      @Temporal(TemporalType.TIMESTAMP)
      @Column(name = "joined", nullable = false, updatable = false)
      @NotNull
      public Date getDateJoined() { return dateJoined; }
      public void setDateJoined(Date dateJoined) {
          this.dateJoined = dateJoined;
      }

      @Temporal(TemporalType.DATE)
      @Column(name = "dob")
      public Date getDateOfBirth() { return dateOfBirth; }
      public void setDateOfBirth(Date dateOfBirth) {
          this.dateOfBirth = dateOfBirth;
      }

      public String getLocation() { return this.location; }
      public void setLocation(String location) {
          this.location = location;
      }
  }

Member is an abstract entity class that holds the username, passwordHash, and emailAddress inherited by the Golfer entity. The Member entity, shown in listing 4.2, uses a joined-inheritance strategy. This design makes it possible to have different types of members that are represented in separate tables. For the purpose of this registration example, we assume that a golfer is the only type of member. Again, don’t get bogged down in this design if you’re new to JPA. Appreciate that the goal here is to establish a JavaBean that can be used to capture data from the registration form.

Listing 4.2. The Member entity class, a superclass for application user types
  package org.open18.model;

  import java.io.Serializable;
  import javax.persistence.*;
  import org.hibernate.validator.*;

  @Entity
  @Inheritance(strategy = InheritanceType.JOINED)
  @Table(name = "MEMBER", uniqueConstraints = {
      @UniqueConstraint(columnNames = "username"),
      @UniqueConstraint(columnNames = "email_address")
  })
  public abstract class Member implements Serializable {
      private Long id;
      private String username;
      private String passwordHash;
      private String emailAddress;

      @Id @GeneratedValue
      public Long getId() { return id; }
      public void setId(Long id) {
          this.id = id;
      }

      @Column(name = "username", nullable = false)
      @NotNull @Length(min = 6)
      public String getUsername() { return username; }
      public void setUsername(String username) {
          this.username = username;
      }

      @Column(name = "password_hash", nullable = false)
      @NotNull
      public String getPasswordHash() { return passwordHash; }
      public void setPasswordHash(String passwordHash) {
          this.passwordHash = passwordHash;
      }

      @Column(name = "email_address", nullable = false)
      @NotNull @Email
      public String getEmailAddress() { return emailAddress; }
      public void setEmailAddress(String emailAddress) {
          this.emailAddress = emailAddress;
     }
  }

The registration form needs to capture a plain-text password from the user as well as a password confirmation. Corresponding properties aren’t found on either the Golfer or the Member entity since these fields aren’t to be persisted. Rather than dirtying the entity classes with transient fields, we’ll put these fields on a reusable JavaBean, PasswordBean, defined in listing 4.3. The PasswordBean also contains a business method for verifying that the two passwords entered are equivalent. This class is created under the src/model directory of the seam-gen project along with the entity classes.

Listing 4.3. A Seam JavaBean component that holds and verifies a new password
  package org.open18.auth;

  import org.jboss.seam.annotations.Name;

  @Name("passwordBean")
  public class PasswordBean {
      private String password;
      private String confirm;

      public String getPassword() { return password; }
      public void setPassword(String password) { this.password = password; }
      public String getConfirm() { return confirm; }
      public void setConfirm(String confirm) { this.confirm = confirm; }

      public boolean verify() {
          return confirm != null && confirm.equals(password);
      }
  }

To give you a true appreciation of how easy Seam is making your life, I want to now show you how @Name and @Scope provide everything necessary to design a JSF form and action bean component to process the form submission. No XML is required.

4.4.2. Preparing an action bean component

Return once again to the Seam distribution directory. Execute the command seam new-action to create the RegisterAction component using the following responses:

  Seam component name: registerAction
  Bean class name: RegisterAction
  Action method name: register
  Page name: register

This command generates the RegisterAction JavaBean class shown in listing 4.4. The @Name annotation above this class makes it a Seam component. Since the @Scope annotation is excluded, and this is a regular JavaBean, instances of it are bound to the event context. (The Seam annotations @In and @Logger are described later in this chapter.) This component will serve as an action bean component—a component that provides action methods used by UI command components. A Seam component used for this purpose completely replaces the need for a JSF managed bean.

The RegisterAction component contains a single method, register(), that will be used as the target of the form on the register.xhtml page, also generated by the new-action command. Although the register() method is just a stub, it will suffice for now. You’ll develop the RegisterAction component and register.xhtml page further as you progress through the chapter.

Listing 4.4. The Seam component that handles the member registration
  package org.open18.action;

  import org.jboss.seam.annotations.*;
  import org.jboss.seam.log.Log;
  import org.jboss.seam.faces.FacesMessages;

  @Name("registerAction")
  public class RegisterAction {

      @Logger private Log log;

      @In private FacesMessages facesMessages;

      public void register() {
          log.info("registerAction.register() action called");
          facesMessages.add("register");
      }
  }

Before taking another step down the development path, we need to safeguard ourselves by creating a test. Fortunately, seam-gen has already done the legwork for us.

4.4.3. Integration testing components

To practice good agile development techniques, you always want to create a test either before or while you’re developing a new component. Conveniently, the new-action command also generated an integration test class, RegisterActionTest, in the src/test directory. The test class, shown in listing 4.5, has been renamed to RegisterGolferIntegrationTest to better represent its function as an integration test.

Listing 4.5. A TestNG-based Seam integration test

The test class in listing 4.5 extends SeamTest, which bootstraps the Embedded JBoss to provide a Java EE–compliant environment in which to test your components. The FacesRequest anonymous inner class is used to emulate the JSF life cycle, shown here passing through the Invoke Application phase. seam-gen projects use the testing framework TestNG. A TestNG configuration file, RegisterActionTest.xml, is created along with this class to configure the test runner. A modified version that takes into account the renamed test class is shown here:

  <!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd">
  <suite name="RegisterAction Tests" verbose="2" parallel="false">
    <test name="RegisterAction Test">
        <class name="org.open18.test.RegisterGolferIntegrationTest"/>
      </classes>
    </test>
  </suite>

The Ant target named test in the project’s build.xml file looks for files ending in Test.xml and feeds them into the TestNG test runner to execute. You should be able to run ant test from the root of the project to verify that the test passes, producing the output shown here:

  test:
    [testng] [Parser] Running:
    [testng]   /home/twoputt/projects/open18/test-build/RegisterAction.xml
    [testng]
    [testng] INFO  [org.open18.action.RegisterAction] registerAction.register() action called
    [testng] PASSED: test_register
    [testng]
    [testng] ===============================================
    [testng]     RegisterAction Test
    [testng]     Tests run: 1, Failures: 0, Skips: 0
    [testng] ===============================================
    [testng]
    [testng]
    [testng] ===============================================
    [testng] RegisterAction Tests
    [testng] Total tests run: 1, Failures: 0, Skips: 0
    [testng] ===============================================
    [testng]

  BUILD SUCCESSFUL
  Total time: 12 seconds

You can adjust the log levels used during a test run by editing the Log4j configuration bootstrap/log4j.xml. The file src/test/readme.txt contains instructions on how to run a Seam integration test from Eclipse (it requires that you have Embedded JBoss on the test classpath).

 

Note

The Embedded JBoss bundled with Seam 2.0 only works with a Java 5 runtime (not a Java 6 or 7 runtime). Until a version of Seam is released with a Java 6–compatible Embedded JBoss container, you must run tests using Java 5.

 

 

What about unit tests?

Seam favors the use of integration tests, not unit tests, for validating the behavior of action bean components. This approach aligns with Seam’s goal to eliminate unnecessary layers. These integration tests are necessary if your action bean component works with an ORM or JSF directly. You can use Seam to create a well-layered application, thus allowing you to create unit tests at the boundaries of every layer. Seam just helps you get an application up and running quickly, and for that, integration tests are the tests that matter the most.

If you believe strongly in the fact that your unit tests should run without any external dependencies—such as the Embedded JBoss runtime—and your action bean component depends on functionality provided by the Seam container, it’s possible to bootstrap a mock Seam container. Refer to the Seam test suite for examples of how this is done. If you venture down that path, I encourage you to have a framework like EasyMock handy to mock built-in Seam components that are harder to make work in isolation.

 

The action bean component RegisterAction is just a stub at this point, but it’s good enough to turn to the task of creating the JSF template that renders the registration form. Using test-driven development (TDD) principles, we’ll complete the implementation of the register() method when we need it—not a minute sooner.

4.4.4. Hooking components into JSF

Now we need to set up JSF so that it can access the Seam components. Guess what? There’s nothing to do! Believe it or not, any Seam component is accessible to JSF as is (see the accompanying sidebar). The @Name annotation can be compared to defining a JSF managed bean in the faces-config.xml descriptor, except that the resulting component is far more capable. While you may look back and see that you’ve entered quite a bit of code, you haven’t had to write a single line of XML. And there’s no need to mess with glue code, either. Golfer and PasswordBean can serve as backing beans, and RegisterAction can provide the action method for the registration page. All you need to do is write a JSF view to use them. Let’s enhance the registration view generated by seam-gen to capture the input necessary to register a new member.

 

Resolving Seam components from JSF

Seam establishes a bridge between JSF and its own container by registering a custom expression language (EL) resolver with the JSF 1.2 application runtime. The configuration for this resolver is defined in the faces-config.xml descriptor in the Seam core JAR file:

     <faces-config>
       <application>
        <el-resolver>org.jboss.seam.el.SeamELResolver</el-resolver>
       </application>
     </faces-config>

When a Seam component name is used as the root—the first segment—of an EL expression, Seam either locates or creates a component instance and makes it available to the variable resolver. For instance, the EL resolver divides the expression #{passwordBean.password} into the method getPassword() on a Seam component named passwordBean. If the custom resolver cannot find a matching Seam component, it passes the torch back to the default JSF variable resolver.

 

JSF views, Seam-style

The register.xhtml page was created with a basic JSF form when you ran the new-action command, but we need to add input fields to it. The augmented form is shown in listing 4.6. The register() method on the RegisterAction component serves as the form’s action, as defined by the method-binding expression #{registerAction.register} in the action attribute of the UI command button. This method-binding expression is derived by combining the component name of the action bean component, registerAction, with the name of the action method, register (minus the parentheses). Seam also prepares an instance of the Golfer entity class, binding it to the context variable newGolfer, and an instance of the PasswordBean JavaBean class, binding it to the context variable passwordBean, which are both used to capture data from the input fields.

Listing 4.6. The golfer registration form
  <h:form id="registerActionForm">
    <rich:panel>
      <f:facet name="header">Open 18 Member Registration</f:facet>
      <s:decorate id="firstNameField" template="layout/edit.xhtml">
         <ui:define name="label">First name</ui:define>
         <h:inputText id="firstName"
           value="#{newGolfer.firstName}" required="true"/>
       </s:decorate>
       <s:decorate id="lastNameField" template="layout/edit.xhtml">
         <ui:define name="label">Last name</ui:define>
         <h:inputText id="lastName"
           value="#{newGolfer.lastName}" required="true"/>
      </s:decorate>
      <s:decorate id="emailField" template="layout/edit.xhtml">
        <ui:define name="label">Email address</ui:define>
        <h:inputText id="emailAddress"
          value="#{newGolfer.emailAddress}" required="true"/>
       </s:decorate>
       <s:decorate id="usernameField" template="layout/edit.xhtml">
        <ui:define name="label">Username</ui:define>
        <h:inputText id="username"
          value="#{newGolfer.username}" required="true"/>
       </s:decorate>
       <s:decorate id="passwordField" template="layout/edit.xhtml">
        <ui:define name="label">Password</ui:define>
        <h:inputSecret id="password"
          value="#{passwordBean.password}" required="true"/>
      </s:decorate>
      <s:decorate id="confirmField" template="layout/edit.xhtml">
        <ui:define name="label">Confirm password</ui:define>
        <h:inputSecret id="confirm"
          value="#{passwordBean.confirm}" required="true"/>
      </s:decorate>
      <s:decorate id="dateOfBirthField" template="layout/edit.xhtml">
        <ui:define name="label">Date of birth</ui:define>
        <rich:calendar id="dateOfBirth"
          value="#{newGolfer.dateOfBirth}"/>
      </s:decorate>
      <s:decorate id="genderField" template="layout/edit.xhtml">
        <ui:define name="label">Gender</ui:define>
         <h:selectOneRadio id="gender" value="#{newGolfer.gender}">
           <s:convertEnum/>
           <s:enumItem enumValue="MALE" label="Male"/>
           <s:enumItem enumValue="FEMALE" label="Female"/>
        </h:selectOneRadio>
       </s:decorate>
       <s:decorate id="locationField" template="layout/edit.xhtml">
         <ui:define name="label">Location</ui:define>
         <h:inputText id="location" value="#{newGolfer.location}"/>
      </s:decorate>
      <div style="clear:both">
        <span class="required">*</span> required fields
      </div>
    </rich:panel>
    <div class="actionButtons">
      <h:commandButton id="cancel" value="Cancel"
        action="home" immediate="true"/>
      <h:commandButton id="register" value="Register"
        action="#{registerAction.register}">
        <s:defaultAction/>
      </h:commandButton>
    </div>
  </h:form>

This form should appear familiar to you since the markup, particularly <s:decorate>, was covered in the previous chapter. Let’s focus on how the form data is exchanged with our components. Value-binding expressions that take the form #{newGolfer.username} are a two-way street. They’re used to output a component property to the screen as well as capture a value to be assigned to the property when the form is submitted. This form captures data from the user and assigns the values to the properties of the Seam components bound to the newGolfer and passwordBean context variables.

This JSF template takes advantage of several UI components not yet covered. The <s:convertEnum> and <s:enumItem> Seam UI component tags translate Java 5 enum type properties to and from string values. The <rich:calendar> component tag from RichFaces lets the user select a date using a pop-up calendar. The <s:defaultAction> Seam UI component tag sets which button is activated when the user presses the Enter key. This overrides the default browser behavior of associating the first submit button in the form with the Enter key, which in this case would be the Cancel button. For a complete list of component tags that Seam adds to JSF, consult the Seam reference documentation.

As you can see, annotations and Seam UI component tags dramatically reduce the amount of work necessary to pull together a JSF application. While we still need to provide an implementation for the register() action method, the @Name annotation is the only link needed to get JSF working with your Seam components.

A component lives a busy life outside of these moments in the limelight. In the next section, you’ll get a glimpse behind the scenes of the life of a component: how it’s discovered, selected, groomed, and managed by the Seam container.

4.5. A component’s life

Before a component definition can be used to spawn component instances, the definition of the component must be discovered by the Seam container. Even upon discovery, the component may not be loaded into the Seam container if its prerequisites aren’t satisfied. Once loaded, Seam will create an instance from the definition immediately if it’s a startup component or wait for it to be requested if not. Regardless of when instance creation occurs, its life-cycle callback methods are invoked before the instance is returned to the requester. Finally, when the component is destroyed or goes out of scope, it has one last opportunity to perform work before being cast away. That’s a component’s life; let’s start from the birth.

4.5.1. Loading component definitions

In order for the components to get into the container, Seam has to find them. This happens during the Seam initialization process. The means by which Seam is bootstrapped is covered in the previous chapter. During initialization, a deployment scanner scours the classpath looking for classes that host the @Name annotation. In addition to Java classes, Seam accepts both compiled and noncompiled Groovy classes. Seam also looks for classes that are identified as components in the XML-based component descriptor and loads them into the container. The component descriptor is covered in depth in the next chapter.

For each class declared as a component, Seam creates a component definition and stashes it away in the application scope. The name of the attribute under which the component definition is stored is derived by appending .component to the component name (e.g., registerAction.component). For your purposes, you’ll always address the component by its component name (e.g., registerAction).

Many XML configurations were devised because the Java language lacked a common syntax for adding class metadata that is detectable by the classloader. That changed when annotations were introduced. The component scanner frees you from having to declare every Seam component in an XML descriptor because it’s capable of seeking out classes that host the @Name annotation. The result is that you have one less XML file to juggle (and no unnecessary layer of abstraction).

Ah, but there’s a catch! Seam only considers qualified classpath entries (class directories and JAR files). A classpath entry is considered qualified if it contains a seam.properties file at its root or the META-INF directory contains a component descriptor (i.e., components.xml). In the next chapter, you’ll discover that the seam.properties file has another use: to initialize the properties of Seam components. Figure 4.3 shows the presence of the seam.properties file at the root of classpath for the open18.jar. If you have Seam components deployed and they aren’t being picked up, the first thing to verify is that a seam.properties file is present on the classpath where your Seam components reside.

Figure 4.3. Seam will scan this classpath entry since it contains the seam.properties marker file.

Requiring the presence of a marker file is a JVM classloader optimization that works to pare down the number of classpath entries that must be scanned for components. Though it may seem annoying to have to ensure that a marker file is present, this annoyance pays off in that it helps Seam figure out which classpath entries are relevant. Without this optimization, Seam would go looking all over the classpath for components, possibly even stepping into the application server classpath, an expensive and potentially error-prone operation. By using the classpath markers, Seam knows exactly where to look.

It is possible that even though a class is in a qualified classpath entry and has a @Name annotation, or it’s declared as a component in the component descriptor, it still won’t be recognized as a Seam component. The next section details how to define prerequisites on a class that make its installation conditional.

4.5.2. When to @Install a component

When the component scanner finds a class annotated with @Name, the default behavior is to make it a component. While the automatic discovery of components is a powerful mechanism, you lose a degree of flexibility over which classes are turned into components. That’s where the @Install annotation comes into play. This @Install annotation, summarized in table 4.8, tells Seam the conditions under which to honor a component declaration. It can also be used to allow a second definition of the same component to override the first. Both cases will be considered in detail.

Table 4.8. The @Install annotation

Name:

Install

Purpose:

Used to define a set of prerequisite conditions necessary for a component declaration to be accepted and the component registered with the Seam container (i.e., installed).

Target:

TYPE (class)

Attribute

Type

Function

value boolean A flag indicating whether to install the component. Subsequent conditions may still prevent the component from being installed. Default: true.
dependencies String[] The names of other components that must be installed for this component to be installed. Default: none.
classDependencies String[] Classes that must be available on the classpath for this component to be installed. Classes are provided as strings to avoid unnecessary compilation requirements. Default: none.
genericDependencies Class[] Classes that must be acting as components for this component to be installed. Default: none.
precedence int A weighted value used to compare against other components assigned the same name. The component of higher precedence will be installed. Default: 20.
debug boolean Indicates that this component should only be installed when Seam is operating in debug mode. Default: false.
       

You have a wide range of prerequisites for controlling the condition under which a component is installed. The most clear-cut is the value attribute on the @Install annotation, which is a boolean that can be used to switch the component on or off.

You can further control whether the component is installed by enforcing any of the following prerequisites:

  • The presence of other component definitions, looked up by component name
  • The presence of other component definitions, looked up by class name
  • Classes available on the classpath
  • A weighted precedence value (selects one definition over another for the same component name and precedence combination)
  • The Seam debug mode setting

If the prerequisites are not satisfied, that doesn’t mean that its future as a component is entirely bleak, though. You can still place it back into the ranks of the other components by declaring it in the component descriptor. Several built-in Seam components are declared using @Install(false), allowing you to enable them as needed. A sampling of components include

  • Seam managed persistence context
  • jBPM session factory
  • POJO cache
  • Asynchronous dispatcher (Quartz, EJB 3, Spring)
  • Non-JTA transaction manager
  • JMS topic publisher
  • Spring context loader

Aside from limiting the set of component definitions, conditional installation can be useful for selecting between alternate implementations of a component.

Alternate implementations

There are times when you need to perform different logic to support different implementations of the same API, such as the JSF specification or application server environment. To keep your component clean, void of conditional logic that checks for the presence of an implementation class, you may choose to separate the logic for each implementation into different components and have the appropriate component selected by Seam according to the prerequisites defined in the @Install annotation.

To make use of the @Install annotation in this case, you create two implementation classes and one interface. Then you give the two implementations the same component name, and let the @Install annotation handle which one will be configured based on the presence of a particular JSF implementation class.

You can create a component for the Sun JSF implementation:

  @Name("jsfAdapter")
  @Install(classDependencies = "com.sun.faces.context.FacesContextImpl")
  public class SunJsfAdapter implements JsfAdapter {...}

and another for the MyFaces JSF implementation:

  @Name("jsfAdapter")
  @Install(classDependencies =
    "org.apache.myfaces.context.servlet.ServletFacesContextImpl")
  public class MyFacesJsfAdapter implements JsfAdapter {...}

You can then request the component named jsfAdapter from the Seam container and Seam will return the appropriate implementation for you depending on which FacesContext implementation class is available on the classpath.

How many frameworks completely overlook this type of functionality, forcing you to devise your own solution? Conditional installation is a fundamental part of defining components.

 

Note

Seam doesn’t allow EL value expressions to be used in the value attribute of the @Install annotation. However, you can put a replacement token (a name surround by a pair of @ symbols) in the installed attribute on the <component> element and then have your build supply an environment-specific value for that token in the components.properties file. You’ll learn how to use replacement tokens in the next chapter.

 

There is another facet to alternate implementations: you can define components that should only be available during development mode, possibly ones that override equivalently named production components.

Debug mode components

Another way to control the installation of a component is to tie it to Seam’s debug mode flag. You do so by setting the debug attribute on the @Install annotation to true. Seam’s debug mode is controlled by the debug property on the org.jboss.seam.core.init component. You enable the debug mode flag by adding the following declaration to the component descriptor:

  <core:init debug="true"/>

You’ll learn how to configure built-in Seam components using XML in the next chapter. For now, let’s focus on the effect of this setting. When it’s set to true, Seam activates components that are marked with @Install(debug=true). You can use this flag to swap in components that return canned data or otherwise stub out back-end logic. In debug mode, the debug component has higher priority. When it comes time to deploy to a production environment, the debug component is disabled, and if a non-debug component with the same component name exists, it becomes activated.

Speaking of priority, one component definition can be selected over another based on its precedence. A precedence value is required any time you have two components assigned to the same component name. Let’s see how Seam handles the curve ball of conflicting component definitions.

Installation precedence

Precedence defines which component definition wins when two components try to occupy the same space—in other words, they have the same component name. A precedence is an integer value assigned to a component using the precedence attribute on the @Install annotation. The higher the value, the more clout it has. All built-in Seam components have a precedence of Install.BUILT_IN (0), so they can easily be overridden. If a precedence isn’t defined, it defaults to Install.APPLICATION (20). With precedence in the picture, the rule is that two components can’t be defined with the same name and precedence value. If this situation occurs, it will cause an exception to be thrown at startup when the component scanner discovers it.

If all the prerequisites are satisfied, the component gets the gig. It has made it into the container. A single class can also produce multiple component definitions with different component names and potentially different scopes. These alternate definitions are known as component roles.

4.5.3. Giving a component multiple @Roles

As you know, a component must be assigned a name. But that doesn’t mean it can’t be assigned more than one name. Alternate name and scope combinations are assigned to a component using the @Role annotation, summarized in table 4.9. To define multiple @Role annotations for a single component, you nest them within the @Roles annotation.

Table 4.9. The @Role annotation

Name:

Role

Purpose:

Associates the component with an alternate name and scope. Multiple roles are nested within the @Roles annotation.

Target:

TYPE (class)

Attribute

Type

Function

name String An alternate name for this component. A new instance of this component is created when this alternate name is requested, independent of any instance bound to its primary name. Default: none (required).
scope ScopeType The Seam context in which an instance of this component is stored for this role. Table 4.6 lists the default value according to component type.

The idea behind roles is to allow the same component to be instantiated and managed by Seam for different purposes. A scope is assigned to a role in order to relieve the code that uses the role name from making the decision of where to store the new instance. You’ll often see this technique used in outjection, covered in chapter 6.

Multiple roles also allow you to use multiple instances of the same component class simultaneously in the same scope. Let’s consider a simple example when such dualism is needed. The registration form is using an instance of the Golfer component, named newGolfer, to capture the new member information. Suppose that we want to use an example query, fed with another instance of the Golfer class, that allows the registering member to locate the member that referred them to the website. To implement this feature, the Golfer component needs to be accessed under two different names, newGolfer and golferExample. When the user clicks the lookup button, the search criteria get applied to the auxiliary Golfer instance bound to the golferExample context variable and passed on to the back end to perform the example query. The alternate name is assigned to the Golfer class using a @Role annotation, shown here in bold:

  @Name("newGolfer")
  @Scope(ScopeType.EVENT)
  @Role(name = "golferExample", scope = ScopeType.EVENT)
  public class Golfer extends Member { ... }

Example queries are supported natively in Hibernate but not in JPA. Here’s how an example query is conducted using Hibernate:

  List<Golfer> existingGolfers = (List<Golfer>) session
      .createCriteria(Golfer.class)
      .add(Example.create(golferExample)).list();

Later in this chapter, you’ll learn how to access a component instance populated from a UI form in your action bean component. For now, keep in mind that the role lets you isolate the instance of Golfer used for the example query from the instance used to back the registration form.

Returning to the component scanner, once it finishes addressing all the component definitions and role assignments, the Seam container is left with a bunch of component definitions. But there aren’t yet any component instances. Typically, a component definition has to wait until its component name is requested in order to be instantiated. There’s one condition when the instance of the component is created even though it’s not explicitly requested: if the component is a startup component.

4.5.4. Instantiating components at @Startup

The @Startup annotation, summarized in table 4.10, instructs Seam to take the initiative of creating an instance of the component when the component’s scope is initialized. At the time of this writing, only application- and session-scoped components can be flagged as startup components, though other scopes may be added in the future.

Table 4.10. The @Startup annotation
Name: Startup
Purpose: Instructs the container to eagerly instantiate a component at system initialization for application-scoped components or when the session starts for session-scoped components.
Target: TYPE (class)
Attribute Type Function
depends String[] The names of other components that should be started before this one, if they’re available. Dependencies must be in the same scope as the component itself. Default: none.

If you add the @Startup annotation to the class definition of a component, and the component is scoped to the application context, Seam automatically creates an instance of the component when the application starts. This eager instantiation is consistent with the default behavior of singleton Spring beans. The instance is available as a context variable for the lifetime of the container and thus doesn’t have to be instantiated when requested. The @Startup annotation is well suited for components that use the singleton design pattern.

 

Warning

Application-scoped components aren’t well suited as business objects. Because they’re shared among all threads and synchronizing them would be extremely expensive, storing client-specific information in them is out of the question. Without state, their use in a long-running business use case is limited. Despite this general rule, the @Startup hook is useful for thread-safe resources such as a Hibernate SessionFactory or JPA EntityManagerFactory that you do want in the application scope because they are expensive to initialize.

 

If the component flagged with the @Startup annotation is scoped to the session context, Seam automatically creates an instance of the component when the HTTP session starts. This functionality is a unique Seam feature. It enables you to have components that are automatically instantiated on a per-user basis.

The depends attribute on the @Startup annotation can be used to control the order in which other startup components are instantiated (though as a side effect it can also result in components being started even if they aren’t defined as startup components). The dependent components are supplied as a list of component names in the order that you want them to start.

Regardless of whether the component is instantiated eagerly by the container or it waits for the component name to be requested by the application, Seam handles instance creation. While it is nice to have Seam handle these details for you, there are times when you need to be there to help out with the creation of the instance or to perform custom cleanup when the instance is being destroyed. Component life-cycle callback methods give you that chance.

4.5.5. Component life-cycle callbacks

At the beginning of this chapter, I mention that one of the benefits of using the Seam container to instantiate your classes is that it manages the life cycle of the instance. You can add code that participates in the life cycle by registering two special life-cycle methods. One method is invoked when the component instance is created and another when it’s destroyed. These methods are identified through the use of annotations (the method name is therefore irrelevant). Note that there can only be a single create method and a single destroy method per component.

The method on a component annotated with @PostConstuct is called after an instance has been created and initialized (meaning after the initial property values have been applied). The @PreDestroy method is called before the component instance is removed from the context in which it resides. Both of these annotations are part of the Java EE 5 API. When working with a non-EJB component, you can use either the standard Java EE annotations or their synonyms from the Seam API. The @Create annotation stands in for the @PostConstruct annotation and the @Destroy annotation stands in for the @PreDestroy annotation in non-Java EE 5 environments, as explained in table 4.11.

Table 4.11. The component life-cycle methods per environment

When called

Java EE environment

Non-Java EE environment

After initialization of the component instance @PostConstruct @Create
Before component instance is removed from the Seam context @PreDestroy @Destroy

Note that Seam takes care of invoking the create and destroy methods on JavaBean components while the EJB container handles this task for EJB 3 components. It’s just that with Seam JavaBean components, you have a choice as to which annotation set to use.

 

Note

Any of the life-cycle methods called by Seam can be a no-argument method or accept the Seam component definition—an instance of org.jboss.seam.Component—as its sole parameter. The getName() method on the component definition provides access to the name of the component, which may be useful during an initialization routine.

 

Let’s consider a simple example of when the create and destroy life-cycle methods are used. In the following code, the RegisterAction component writes to the log file whenever it’s created and destroyed:

You can also have collaborator components tap into the life cycle of a component by observing events raised by the container when an instance is created and destroyed. Events are also raised when the instance is bound to and unbound from a context variable. You’ll learn about component events in the next chapter.

The create method is the component way of writing a constructor. It’s especially useful for performing postinitialization logic on the component, such as validating its state or loading auxiliary resources that it needs. If you add the @Startup annotation to the component, the create method becomes a way to perform logic when the application starts or when the user’s session begins, depending on whether the component is application- or session-scoped. Startup logic is useful for performing tasks such as

  • Starting an in-memory database and loading it with seed data
  • Applying a database upgrade script
  • Running an indexing service (i.e., Lucene)
  • Starting up a third-party container, library, or service (i.e., Spring, JMS, jBPM)

The @Install(debug=true) combined with @Startup can be useful for seeding a database in a test environment, as an alternative to Hibernate’s import.sql. You can also have a non-startup component execute logic when the application starts by observing the Seam container postinitialization event (org.jboss.seam.postInitialization).

JavaBean components also support the Java EE standard @PrePassivate and @PostActivate annotations. Methods on a JavaBean marked with these annotations are invoked by Seam when the HTTP session is migrated between nodes in a cluster.[4] If these annotations are used on an EJB component, Seam has no say in the matter and the EJB container takes on the task of invoking these methods.

4http://wiki.jboss.org/wiki/HttpSessionReplication

Although components are the divas of the Seam container, even rock stars need agents. Let’s look at how components get connected to one another.

4.5.6. Wiring components together

Since Seam is a lightweight, dependency injection facilitator, you may be eager to know how components are wired together. I hate to disappointment you, but this is going to be a short section. Dependency injection is one of Seam’s biggest features and there’s too much to get into right now. This brief introduction will give you a glimpse of what you need to know.

The primary means of wiring Seam components together in Seam is a mechanism called bijection, which is explained in detail in chapter 6. Bijection is controlled using annotations. The @In annotation placed on a field or JavaBean property of a component tells Seam to assign to that property the value of a component instance whose component name matches the name of the recipient property, commonly referred to as injection. The bi prefix is used in the name because in addition to injecting components, the inverse is possible. The @Out annotation placed on a field or JavaBean property of a component declares that the value of that property is to be bound to a context variable of the same name.

Bijection is a new approach to inversion of control. Seam also supports the traditional dependency injection mechanism—dubbed static dependency injection, which is controlled using the component descriptor. You’ll learn about static dependency injection in the next chapter. There’s one case when Seam uses an annotation to perform static dependency injection: the @Logger annotation injects a logger instance at component creation time.

Injecting a @Logger

Seam can automatically create a logger that’s configured exclusively for a component. This feature should be a welcome relief for anyone who is tired of fiddling with mundane logger declarations. But Seam also offers some other nice enhancements. Just like Apache’s commons-logging, Seam’s logger implementation supports different logging providers. What makes Seam’s logger unique is that it lets you use value expressions inline in the log messages. These smart log messages reduce the tedium of providing contextual information in the message (the name of the authenticated user, the account name, the order ID, and so on).

To have Seam inject a Log instance into a property of a component, simply place that @Logger annotation above the property whose type is org.jboss.seam.log.Log:

  @Name("registerAction")
  public class RegisterAction() {
      @Logger private Log log;
      ...
  }

The Log instance is injected after the component is instantiated but before the @PostConstruct method is invoked. Table 4.12 summarizes the @Logger annotation.

Table 4.12. The @Logger annotation
Name: Logger
Purpose: Injects a Seam Log instance into this field when the component is instantiated.
Target: FIELD (of type org.jboss.seam.log.Log)
Attribute Type Function
category String A custom log category for this instance. Default: the fully qualified class name in which this annotation is used.

 

Tip

How do you configure the logging? There’s no change in configuration if you’re using Log4j or the standard JDK logging. Seam uses Log4j if it’s available on the classpath, falling back to standard JDK logging if it’s not. Seam’s Log implementation is merely a wrapper around the existing logging frameworks, adding the convenience of injecting the Log instance via dependency injection and using EL notation in the message.

 

Here’s an example of a log message that uses EL notation:

  log.debug("Registering golfer #{newGolfer.username}");

The messages are considered contextual because they can access any Seam component that’s “in context” at the time the log message is reported. This parallels the use of EL in JSF messages when added using the FacesMessage component.

In section 4.7, you’ll learn how to access a component once it is loaded and ready to perform. For now, let’s skip to the end of a component’s life.

4.5.7. Where all components go to die

Just like any other Java objects, component instances die when they go out of scope. A component instance is destroyed when any of the following occurs:

  • The context it occupies ends
  • The context variable to which it is bound is assigned a null value
  • The instance is explicitly removed using the Seam API

The instance is invoked one last time before it goes out of scope when its destroy method is called by the Seam container. The destroy method was covered in section 4.5.5.

That covers the life of a JavaBean component and the instances that it spawns. Seam is also capable of participating in the life of an EJB session bean component. However, in this case, Seam merely plays the role of collaborator to the EJB container. Let’s see how Seam works with the EJB container to turn EJB session beans into Seam components.

4.6. Using EJB 3 session beans in Seam

After I made so much ado about how Seam stitches EJB 3 and JSF together in chapter 1, you may be wondering why I avoided the use of an EJB 3 session bean in the registration example. If you’re waiting for the big unveiling, I’m sorry to disappoint you. There isn’t much to show. The switch from a JavaBean to an EJB 3 session bean is just a matter of adding a couple of annotations and an interface and voilà, you have an EJB component. Of course, this migration isn’t nearly as simple in EJB 2. Although Seam can work with EJB 2 components, all references to EJB in this book assume the use of EJB 3.

 

Note

In order to use an EJB session bean, your project must be deployed as an EAR. The EJB session beans are packaged as an EJB JAR and the web application is packaged separately as a WAR. These two archives are then bundled together to form an EAR. To create an EAR project, run seam setup again, this time choosing the EAR project option. Unfortunately, by using the EAR format you lose incremental hot deployment of JavaBean components.

 

Up to this point, I’ve presented several areas of Java EE that Seam replaces with its own solution. For instance, the page descriptor replaces the declarative JSF navigation and adds page-oriented features, the @Name annotation replaces the JSF managed bean facility, and the Seam container manages all variable contexts. Apart from these improvements, Seam leverages a great deal of the Java EE standard, which is most apparent in the area of EJB.

In this section, you’ll learn that Seam can derive a component from an EJB 3 session bean—herein referred to as a Seam session bean component. The EJB 3 container does most of the work of managing it; Seam only steps in to bind the component to a context variable and apply its own set of method interceptors. While we are on the topic of ownership, let’s consider who owns a Seam session bean component, the EJB container or Seam?

4.6.1. Whose component is it, anyway?

At first, you might not give the question of who owns a Seam session bean component much thought. The component scanner finds a class with a @Name annotation in an EJB JAR with a seam.properties file and creates a component for it. But the same class has already been picked up by the EJB container, either because it has a @Stateless or @Stateful annotation or it’s declared as an EJB component in an XML descriptor. So, who owns the component, the EJB container or Seam?

The answer is: The EJB container. Still, session beans designated as Seam components have a sort of dual personality. They act as both Seam components and EJB 3 components, taking on the services of both containers. The EJB container manages the session bean, but Seam gets its hands on the session bean’s life cycle, using interceptors to weave in additional services.

Seam session bean components differ from other Seam component types in one fundamental way: Seam doesn’t create instances of session bean components using the default constructor of the class. Instead, the work of instantiating the class is delegated to the EJB container. The EJB container is responsible for managing session bean components in the same way that Seam manages JavaBean components.

When the Seam container determines that an instance of the component needs to be created, Seam asks the EJB container for a reference to the session bean instance using a JNDI lookup. Once Seam has a reference to the session bean instance, Seam adds additional services to it and then binds it to a context variable, just as Seam would do with a JavaBean component instance. The two containers are working together to create and initialize an instance of a session bean component.

 

Note

What about message-driven beans? I am purposefully not discussing message-driven beans (MDBs) in this section. Although an MDB can act as a Seam component, the dynamics are very different. Message-driven beans can’t be instantiated by the application, which means they’re never associated with a context. Instead, they listen for messages on a JMS topic or queue and get instantiated by the EJB container to handle a message when it arrives. They can, however, take advantage of bijection.

 

Let’s take a closer look at how a session bean becomes a Seam component and what it means for the component’s functionality.

4.6.2. The making of a Seam session bean component

A handful of differences exists between a JavaBean component and a Seam session bean component. One set of differences pertains to the requirements for creating an EJB 3 component. The other set of differences relates to how Seam treats these components when compared to other Seam components.

The EJB 3.0 specification requires session beans to implement either a local or remote interface (though this requirement is being removed in EJB 3.1). The interface must be annotated with either @Local or @Remote or must be declared as an EJB 3 interface in an XML descriptor. In order for a method on a session bean component to be accessible to the client (e.g., a Seam application), it must be defined on the EJB 3 interface. It’s not enough just to declare the method as public on the implementing class.

There’s one final requirement that only pertains to stateful session bean components. All stateful session beans acting as Seam components must define a no-arguments method marked with the EJB 3 @Remove annotation. The @Remove method is called when the context containing the session bean reference is destroyed. Seam uses this method to instruct the EJB container to destroy the session bean. If the @Remove method is invoked directly, it leads to an immediate removal of the session bean reference, unless a runtime or remote exception is thrown and the exception class is marked with @ApplicationException (it’s not a system exception) or an exception is thrown that isn’t a runtime or remote exception and the retainIfException attribute on the @Remove annotation is set to true.

 

Note

Note that there is a distinct difference between the @Remove and @PreDestroy annotations. The method marked with the @Remove annotation is called when Seam removes the reference to the instance, and the method marked with the @PreDestroy annotation is called when the EJB 3 container destroys the instance itself.

 

The action bean component for the registration page can be rewritten as a stateful Seam session bean component. Begin by renaming the implementation class to RegisterActionBean, annotating it with @Stateful, and implementing the RegisterAction interface:

  package org.open18.action;

  import ...;
  import javax.ejb.Stateful;

  @Stateful
  @Name("registerAction")
  @Scope(ScopeType.EVENT)
  public class RegisterActionBean implements RegisterAction {
      ...
      @Remove public void destroy() {}
  }

Note that the rest of the class body remains as before since Seam session bean components can still use annotations handled by Seam, such as @Logger and @In.

The next, and final, step is to define the RegisterAction type as the EJB 3 interface and declare the methods that need to be accessible to clients, such as JSF. The method annotated with @Remove must also be defined on the interface. The result is shown here:

  package org.open18.action;

  import ...;
  import javax.ejb.Local;

  @Local
  public interface RegisterAction {
      public void register();
      public void destroy();
  }

 

EJB References

To access an EJB component via JNDI when deploying to a compliant Java EE server, you must register an EJB reference in the web.xml descriptor. The reference is declared using <ejb-ref> for a remote component and <ejb-local-ref> for a local component. The value of <ejb-ref-name>, which is arbitrary, is bound to the java:comp/env namespace in JNDI. Here’s an example of a local reference for RegisterActionBean:

     <ejb-local-ref>
      <ejb-ref-name>open18ee/RegisterActionBean/local</ejb-ref-name>
      <ejb-ref-type>Session</ejb-ref-type>
      <local>org.open18.action.RegisterAction</local>
     </ejb-local-ref>

This step is not required on JBoss AS 4 since the component is automatically registered with JNDI using the pattern: application name/component name/client view type.

 

Unlike JavaBean components, the methods of an EJB component are automatically wrapped in a transaction, unless specified otherwise. You haven’t had to worry about transactions up to this point since Seam automatically wraps each request in a global JTA transaction. However, if you were to disable Seam’s transaction management, the transactional behavior of EJB 3 components would kick in.

Besides the difference in default scopes and which container handles transactions, the persistence context, concurrency, and security, the session bean components operate just like their JavaBean counterparts. Seam truly does make the use of EJBs a preference rather than a design decision. If you determine that you need an EJB feature, such as web services, you can make the switch when necessary. For in-depth coverage of EJB and the features it provides, consult EJB 3 in Action (Manning, 2007). The focus of this chapter is on the integration between Seam and EJB 3 components.

4.6.3. The mechanics of the interaction

Let’s take a closer look at how Seam obtains session bean references from the EJB container and how it participates in the life cycle of the server-side component. There are several references in this section to features of Seam that are covered in chapter 6, such as bijection and interceptors. Feel free to come back to this section once you’ve learned that material. You can safely skip this section if you’re only interested in the high-level view of Seam EJB components right now.

Playing a part in the life of a session bean component

From the moment the Seam component scanner detects a Seam session bean component, Seam begins to participate in its life. Seam taps into the postconstruct logic of the EJB component to register additional server-side method interceptors that decorate the component with services such as bijection, conversation controls, and event handling. Seam brings these features to Seam session beans by registering an EJB interceptor using the following interceptor mapping in the EJB deployment descriptor, META-INF/ejb-jar.xml:

  <ejb-jar xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
     version="3.0">
     <interceptors>
       <interceptor>
         <interceptor-class>
           org.jboss.seam.ejb.SeamInterceptor
          </interceptor-class>
        </interceptor>
      </interceptors>
      <assembly-descriptor>
        <interceptor-binding>
          <ejb-name>*</ejb-name>
          <interceptor-class>
            org.jboss.seam.ejb.SeamInterceptor
          </interceptor-class>
        </interceptor-binding>
      </assembly-descriptor>
  </ejb-jar>

Obviously, you want this type of configuration to be set up automatically, which luckily seam-gen handles for you. As an alternative, you can skip the XML descriptor and install the interceptor on each session bean component individually using the EJB 3 @Interceptors annotation, as follows:

  @Stateful
  @Name("registerAction")
  @Interceptors(SeamInterceptor.class)
  public class RegisterActionBean implements RegisterAction { ... }

Intercepting session bean invocations is only part of the work Seam has to do to expose a session bean as a Seam component. The next step happens when the Seam component scanner comes across the session bean implementation class annotated with @Name (or declared in the component descriptor). The component scanner simply mines the class definition for information, looking for Seam annotations as it normally would for any other component, and then stores the component definition away in the Seam container. At this point, no interaction occurs with the EJB container.

Obtaining a session bean reference

The fusion with the EJB container occurs when the Seam container receives a request for an unassigned context variable that’s associated with a session bean component. To resolve a value, Seam performs a JNDI lookup to get a reference to the corresponding session bean in the EJB container. This lookup is part of the standard EJB mechanism.

 

Warning

In order to use an EJB session bean as a Seam component, you have to let Seam retrieve it from JNDI. The application should not perform the JNDI lookup itself.

 

There are two ways to declare the JNDI name that Seam should use to look up the session bean. You can either specify a name explicitly in the component class or you can define a template that Seam uses to compute the JNDI name for individual components. The first option is the most straightforward, yet also the most tedious. Here we supply the JNDI name on the session bean component explicitly using the @JndiName annotation:

  @Stateful
  @Name("registerAction")
  @JndiName("open18ee/RegisterAction/local")
  public class RegisterActionBean implements RegisterAction { ... }

A summary of the @JndiName annotation is provided in table 4.13.

Table 4.13. The @JndiName annotation
Name: JndiName
Purpose: Supplies the JNDI name that Seam uses to obtain a reference to an EJB component.
Target: TYPE (class)
Attribute Type Function
value String The JNDI name of the EJB component. Default: none (required).
Tuning the JNDI pattern

It’s possible to have Seam resolve the JNDI name implicitly instead of having to declare the @JndiName annotation on every session bean component. However, Seam still needs assistance in dealing with the widely varying JNDI naming conventions across application servers. You provide Seam with a template, which you assign to the jndiPattern property on the built-in init component. You learn how to assign values to properties of a Seam component in the next chapter. For now, just know that the init component is configured in the component descriptor using the <core:init> element. Here’s the template that’s used when deploying to JBoss AS 4, where open18ee is the name of the application:

  <core:init jndiPattern="open18ee/#;{ejbName}/local"/>

Seam uses this template to construct the JNDI name for the EJB component. The #{ejbName} token is not interpreted as an EL expression. Rather, it gets replaced with the first nonempty value in the following list:

  • The value of the name attribute on the @Stateful or @Stateless annotation
  • The unqualified class name of the component
  • The value of the <ejb-name> node in the EJB deployment descriptor or web.xml

Applying these rules to the example, the #{ejbName} is replaced with RegisterAction, making the entire pattern open18ee/RegisterAction/local. If the server uses a JNDI namespace, such as GlassFish, the pattern must include it:

  <core:init jndiPattern="java:comp/env/open18ee/#{ejbName}/local"/>

In Java EE-compliant environments, EJB references are declared in the web.xml descriptor. The jndiPattern merely reflects the naming convention you use for those reference names.

Projects created by seam-gen use the property replacement token @jndiPattern@ for specifying a value for the jndiPattern property in the test environment. The value for the token is defined in the components.properties file. The pattern found in that file is specific to the Embedded JBoss container:

  jndiPattern=#{ejbName}/local

Everything that happens after the lookup of the session bean reference up until the reference to the newly minted instance is returned to Seam is controlled by the EJB 3 container. Let’s consider what happens after that point.

Infusing session bean components with Seam functionality

Once Seam has obtained a client-side reference to the session bean, it wraps the proxy in additional client-side interceptors and stores it as a context variable just like any other Seam component. Unless specified otherwise, the default scope for stateless session beans is the stateless context, and for stateful session beans the default scope is conversation context.

What makes Seam session bean components so unique is that they share a hybrid of functionality from the EJB container and the Seam container. For instance, all methods are, by default, automatically wrapped in a container-managed transaction, courtesy of the Java EE container. But the component can also take advantage of bijection, a service provided by the Seam container. Bijection allows other Seam component instances to be injected into session beans, something that’s more cumbersome to do with EJB 3 alone (you have to pull in objects from JNDI using the Java EE @Resource annotation). You see this same type of crossover with Spring-Seam components in chapter 15 (online). Table 4.14 lists all of the core services available to a hybrid Seam-EJB 3 component (excluding Seam extensions).

Table 4.14. A list of the core annotations available on a hybrid Seam session bean component

Annotation

Provided by

When applied and condition

@Resource Java EE (web tier, EJB 3 tier) Postconstruct, static
@EJB Java EE (EJB 3 tier) Postconstruct, static
@PersistenceContext Java EE (web tier, EJB 3 tier) proxied by Seam Postconstruct, static
@Interceptors Java EE (EJB 3 tier) Around invoke, stateless
@Interceptors (on @interface) Seam Around invoke, stateless or stateful
@AroundInvoke Java EE (EJB tier) Around invoke
@PreDestroy Java EE (EJB tier) Predestroy
@PrePassivate Java EE (EJB tier) Prepassivate
@PostConstruct Java EE (EJB tier) enhanced by Seam Postconstruct
@In, @RequestParameter, Seam Around invoke, dynamic
@DataModelSelection,    
@DataModelSelectionIndex    
@Out, @DataModel Seam Around invoke, dynamic
@Logger Seam Postconstruct, static

The only catch is that by mixing services, your session beans are going to miss the features provided by Seam in a pure EJB 3 environment. You could stick to obtaining a reference to another EJB component using the @EJB resource injection annotation rather than using bijection, for instance. But why? “Upgrading” your EJB 3 environment is just a matter of adding Seam to the classpath and configuring it, so I encourage you to let go of the purist flag and use Seam in combination with the Java EE services if at all possible.

You have now witnessed a day in the life of both JavaBean components and session bean components. It’s time to learn how to get components to participate in your application.

4.7. Accessing components

Seam components are the key enablers for integrating technologies in a Seam application. Consequently, Seam’s forte is providing unified access to those components across the board. There are three ways to ask for an instance of a Seam component from the Seam container. You can use

  • The component name
  • EL notation (binding expression) that references the component name
  • The Java class of the component

Table 4.15 gives a preview of where component instances can be accessed.

Table 4.15. Places where Seam components can be accessed

From where

How accessed

JSF view EL notation
Seam annotation (e.g., @In, @Out) Component name or EL notation
Java code: Component.getInstance() Component name or component class
Java code: Expressions.instance() EL notation
JPQL or HQL query EL notation
JSF message (i.e., FacesMessage) EL notation
Java properties (i18n bundle or component property) EL notation
JavaScript using Seam Remoting Component name, stub instance, or EL notation
Seam component descriptor (e.g., components.xml) Component name, component class, or EL notation
Seam page descriptor (e.g., pages.xml) EL notation
jPDL page flow descriptor EL notation
jBPM business process descriptor EL notation
Spring configuration file Component name or EL notation

In the previous chapter you saw some examples of Seam components being used in page orchestration logic. That’s just the beginning. The EL is used in JSF views, page flows, business process definitions, annotations, and Java code. You’ll begin learning about these options in this section. Let’s start by considering what happens when a component instance is requested.

4.7.1. Access modes

As it has probably been ingrained into you by now, a component is a recipe that describes how to create a Java object that is managed by the Seam container (a component instance). When you request a component, you’re effectively asking the container to hand you back an instance of the component. This request operates in one of two modes:

  • Lookup only— In this mode, Seam searches for a component instance that’s bound to the requested context variable. It either looks for an instance in the specified scope, or, if a scope isn’t provided, it uses a hierarchical search of all the contexts. If an instance cannot be found, a null value is returned—the non-conditional path in figure 4.2. This mode is the default for the @In annotation, covered in chapter 6.
  • Lookup with option to create— In this mode, Seam performs the same search that’s used in the lookup-only mode, but this time, if an instance can’t be found, Seam instantiates an instance according to the component definition and stores it in the context specified by that definition. Instance creation is represented by the optional clause in figure 4.2. When a component is referenced via a value expression, this mode is always used to locate an instance.

There’s one case when Seam creates a component instance when operating in lookup-only mode: if the requested component is an autocreate component and an instance hasn’t already been forged. In this case, a new instance of the component is created, regardless of the mode. An autocreate component is formed by placing the @AutoCreate annotation on the component class (or by setting the auto-create attribute of the component definition declared in the component descriptor to true). Factory components, which you’ll learn about in chapter 6, can also declare autocreate behavior.

Autocreate functionality eliminates the need to specify the create option on the context variable at the point of access, shifting the responsibility to the component definition. The @AutoCreate annotation is summarized in table 4.16.

Table 4.16. The @AutoCreate annotation
Name: AutoCreate
Purpose: Indicates that Seam should automatically instantiate the component when its component name is requested if an instance doesn’t already exist
Target: TYPE (class), PACKAGE

Several methods of accessing a Seam component are summarized in table 4.17. The final three examples involve the @In annotation, which allows the bijection mechanism to supply a component instance to a property of a Seam component. For now, you can think of the @In annotation as shorthand for looking up the component name explicitly using Component.getInstance() and then assigning it to a property of the component class. This table also indicates the conditions under which the instance will be created if it doesn’t exist in the container.

Table 4.17. Ways to access a Seam component

Example usage

Create if doesn’t exist?

Component.getInstance("componentName") Yes
#{componentName} Yes
Component.getInstance(ComponentClass.class) Yes
@In("componentName") No, unless an autocreate component
@In(value="componentName", create = true) Yes
@In("#{componentName}") Yes

That covers the conditions by which Seam will create component instances. Let’s take a closer look at the strategies you can use to access these component instances.

4.7.2. Access strategies

In a Seam application, you’ll find references to components in annotations, EL notation, and via the Seam API. Regardless of which access strategy you use, the lookup always trickles down to a Seam API call. Therefore, let’s study this interaction.

Seam API

Seam offers several static getInstance() methods on the Component class that are capable of locating and creating component instances. But it’s important to understand this means of access because it’s how instances are requested from the Seam container.

You access a component using the Seam API by passing either the component name (e.g., passwordManager) or the Java class object (e.g., PasswordManager.class) to the Component.getInstance() method. (The PasswordManager component, introduced in listing 5.3 in the next chapter, is responsible for hashing the new golfer’s password.) When you supply a Java class, Seam resolves the component name automatically from the component definition and continues the lookup based on the resolved value. Here’s an example of a lookup by component name:

  PasswordManager passwordManager =
    (PasswordManager) Component.getInstance("passwordManager");

You can also access a context variable directly from the context where it is stored. To do so, you use the Context.get*Context() to retrieve the context—where the * is a placeholder for the name of the context—and then use the get() method to pull the component instance out of the context based on the context variable name:

  PasswordManager passwordManager =
    (PasswordManager) Context.getEventContext().get("passwordManager");

The main difference between Component.getInstance() and accessing the instance directly from the context in which it’s stored is that Component.getInstance() creates an instance if one doesn’t exist (unless you pass false as the second argument), whereas accessing the instance using the Context API never creates a new instance. The context API merely gives you direct access to the storage location of context variables.

You can also search across all contexts using the org.jboss.seam.contexts.Context.lookupInStatefulContexts() method:

  PasswordManager passwordManager = (PasswordManager)
      Contexts.lookupInStatefulContexts("passwordManager");

The lookupInStatefulContexts() method is used by Seam to locate a component instance in cases when a scope isn’t explicitly provided.

 

Component names and context variables

Uses of the term context variable in this chapter have been in relation to component names. While it’s true that a component instance is stored in a context variable according to its component name, a context variable can refer to an object not derived from a Seam component. In fact, any of the name-based access strategies discussed in this section happily return non-Seam objects along with those that are managed by Seam. The notable difference between a component name and plain context variable name is that Seam knows how to initialize a Seam component if the search by component name turns up empty, whereas Seam returns null (in this scenario) if the name isn’t associated with a component.

 

Let’s use the Seam API to look up the dependent components needed to complete the registration logic in the register() method of the RegisterAction component. The implementation is shown in listing 4.7. It needs to be paired with a navigation rule to direct the user to a success page when registration is complete, which is not shown here.

Listing 4.7. Using the Seam API to access dependent component instances

This method is abnormally complex because it does not take advantage of bijection. You will appreciate the simplicity that bijection introduces when you see the refactored version of this component in chapter 6. However, there are perfectly justifiable reasons for using the Seam API directly as in this example, such as to reduce the overhead of method interceptors, to obtain a component instance in a test case, or when bijection isn’t available.

Let’s enhance the integration test from earlier to validate the newly implemented registration logic. The updated test case in listing 4.8 uses a mixture of the Seam API and the EL. The form backing beans are populated in the Update Model Values phase, emulating a form submission, and the register() method is invoked in the Invoke Application phase.

Listing 4.8. Using the Seam API and the EL in an integration test
  package org.open18.action;

  import ...;
  public class RegisterActionIntegrationTest extends SeamTest {

      @Test public void registerValidGolfer() throws Exception {
          new FacesRequest("/register.xhtml") {
              @Override protected void updateModelValues() {
                  Golfer g = (Golfer) Component.getInstance("newGolfer");
                  g.setFirstName("Tommy");
                  g.setLastName("Twoputt");
                  g.setUsername("twoputt");
                  g.setEmailAddress("[email protected]");
                  setValue("#{passwordBean.password}","ilovegolf");
                  setValue("#{passwordBean.confirm}","ilovegolf");
              }

              @Override protected void invokeApplication() {
                  String result = invokeMethod("#{registerAction.register}");
                  assert result != null && result.equals("success");
             }
         }.run();
      }
  }

The test should once again succeed, this time accompanied by SQL statements in the log.

You often see the Seam API used to look up a component instance by calling a static instance() method on the component itself. This replaces the use of a static or thread local variable to maintain an instance of an object. Instead, you let the Seam container manage the instance in one of its scopes. I like to call this a scoped singleton, since it’s accessed just as you would access a singleton, except it isn’t necessarily scoped to the lifetime of the application. For instance, the following method can be used to obtain the event-scoped PasswordManager:

  public static PasswordManager instance() {
      return (PasswordManager)
          Component.getInstance(PasswordManager.class, ScopeType.EVENT);
  }

This technique is useful for retrieving an implementation class of an interface by component name. An example of this type of lookup is seen in the Seam transaction component. The instance() method on the org.jboss.seam.transaction.Transaction class returns a JTA UserTransaction subclass instance using Seam’s alternate implementation selection logic described earlier (which installs one of several candidate components):

  public static UserTransaction instance() {
      return (UserTransaction)
          Component.getInstance(Transaction.class, ScopeType.EVENT);
  }

You’ll learn about configuring transactions in Seam in chapter 9. If you’re interested in how this lookup works, I encourage you to dive into the Seam source code.

You have to decide for yourself whether it’s acceptable to interact directly with the Seam API in your business logic. It’s certainly the most efficient way to access Seam components, but if POJO development is important to you, you may prefer the level of abstraction provided by bijection. Again, Seam doesn’t force your hand.

A more ubiquitous and flexible means of component access is through EL notation. That’s our next stop. You’ve already seen several examples of this syntax in this chapter’s examples. Let’s take a closer look.

EL notation

Value- and method-binding expressions are the lingua franca of Seam. The reason EL notation is so appealing is because it cleanly separates component access from the container responsible for serving the component instance. It’s also attractive because it is dynamic and untyped. EL notation is like having a sticker slapped on your code that says Insert Component Here. The rest is up to the EL resolver and, in turn, the container managing the instance. The EL is the key for being able to resolve components from anywhere and makes the references portable to any EL resolver, not just Seam’s.

Value expressions are used for both resolving a component instance and binding to its properties. Value expressions can also be used in JSF messages and log messages if registered using Seam’s FacesMessages and Log components, respectively. In addition to using value and method expressions in the usual places, you can call on the EL using the Seam API. Let’s rewrite the portion of the register() method to use value and method expressions instead of looking up a component instance and acting on it directly. The following snippet shows two ways to interact with the PasswordBean component via the EL:

  Boolean valid = (Boolean)
      Expressions.instance()
          .createMethodExpression("#{passwordBean.verify}")
          .invoke();
  ...
  String password = (String)
      Expressions.instance()
          .createValueExpression("#{passwordBean.password}")
          .getValue();

You can also use a value expression to assign a value. For instance, say that you want to clear the confirm password when it’s wrong. You can once again use a value-binding expression:

  Expressions.instance()
      .createValueExpression("#{passwordBean.confirm}")
      .setValue(null);

We just covered good old-fashioned EL. But Seam offers so much more. Let’s see what Seam does to make the EL even more powerful as an integrator.

Seam’s enhanced EL

The EL is ideal for integrating technologies because it uses a simple, technology-agnostic syntax. It standardizes on the JavaBean property notation for value expressions and no-argument methods for method expressions. Unfortunately, its simplicity is also its downfall. Because it’s at the intersection of so many technologies, you often find that if the EL supported that one additional feature, you’d be able to take the integration to the next level. This longing is especially strong when you’re attempting to implement advanced layouts that require more sophisticated logic, which you encounter in later examples in the book.

Fortunately, Seam supports several enhancements to the EL, provided in part by the JBoss EL library and supplemented by the Seam EL resolver:

  • Parameterized method-binding expressions
  • Parameterized value-binding expressions
  • No-argument event listener methods
  • “Magic” bean properties (properties not present on the class)
  • Projections

The first two enhancements allow you to use method arguments in an EL expression. The arguments are surrounded in parentheses and separated by commas, just like in Java. Each argument is interpreted as a context variable unless it is quoted or is a number. Before looking at examples of parameterized expressions, I want to highlight the fact that Seam goes in the other direction as well by making the FacesEvent argument on JSF event listener methods optional (e.g. ActionEvent, ValueChangeEvent, and so on). This feature lets you implement an action or event listener without tying your UI logic directly to the JSF API.

You can use a parameterized method binding to pass data to a method on a Seam component. Using the example from earlier in the chapter, you can change the register() action method to pass the newGolfer and passwordBean context variables as arguments:

  <h:commandButton id="register" value="Register"
       action="#{registerAction.register(newGolfer, passwordBean)}"/>

Be aware that the context variable is resolved when the action method is invoked, not when the button in which it’s used is rendered. Thus, the parameters should be names of proper Seam components that will be available on the next request.

You can also pass parameters to value-binding expressions. However, note that when doing so you have to use the full method name of the property (i.e., getName()), not the shorthand syntax (name). Say you’re creating a page in which you want to display the collection of tees for a particular hole on the golf course. The following value expression gives you access to calculated data that otherwise wouldn’t be attainable with standard EL:

  #{teeSet.getTeesByHoleNumber(10)}

Parameterized method- and value-binding expressions allow Seam components to serve as a function library for use in a JSF view, circumventing the need to have to go through the formal process of registering EL functions. A prime example is custom string manipulation, perhaps to truncate a string, leaving a trailing ellipsis if it exceeds a maximum length:

  #{stringUtils.truncate(facility.name, 10)}

You can also call a method on the model to execute domain-specific Boolean logic:

  <h:graphicImage value="/img/signature.gif"
    rendered="#{course.isSignatureHole(hole)}"/>

The parameterized syntax also provides access to methods that don’t follow the JavaBean property syntax. For instance, that pesky size() method on collections and equally evasive length() method on strings aren’t reachable using a standard value expression. But you can use the parameterized method syntax to get there:

  #{course.holes.size()}
  #{course.name.length()}

Recognizing that there are a handful of collection methods that are extremely useful but happen to not follow the JavaBean naming convention, Seam developers have weaved them into the Seam EL resolver as “magic” methods. These properties are summarized in table 4.18, which map to equivalently named no-argument methods on the Java type.

Table 4.18. The magic bean properties supported by the Seam EL resolver

java.util.Collection

java.util.Map

javax.faces.DataModel

size entrySet empty
  keySet size
  size  
  values  

The Seam EL resolver provides direct access to Seam context maps, which are referenced as the root of an EL expression. The name of the map for each context is derived by appending Context to the lowercase name of the context. For instance, eventContext is a map of variables in the event context. You can access the PasswordBean instance via EL using #{eventContext.passwordBean} or #{eventContext["passwordBean"]}.

The parameterized syntax for value and method expressions is only available if you’re using JSP 2.1 (or later) or Facelets (another compelling reason for using Facelets). The optional FacesEvent argument on action and event listeners and magic methods are available across the board. Because of limitations in the JSP compiler, the final enhancement, projections, is only available for expressions appearing within strings in Java code, within component tag attributes in Facelets view templates, or in Seam descriptors.

Projections allow you to mine a collection for values. For instance, suppose you want to get all of the colors used for tee sets on a course:

  #{course.teeSets.{ts|ts.color}}

The ts acts as the iterator variable for the teeSets collection, and the pipe character (|) separates the iterator variable from the nested expression. It’s equivalent to the following pseudocode:

  List result = new ArrayList()
  for (ts : course.teeSets) {
      result.add(ts.color)
  }
  return result;

Projections work for any collection type (list or set), map (entries), or array. The resulting type is always a java.util.List (providing a convenient way to convert a Set to a List) Though not demonstrated in this example, the value expression used in the projection can use the parameterized syntax described earlier.

Projections can also be nested. This allows you to descend into collections returned by the collections, with each level of nesting using the same pattern of iterator variable and nested expression separated by the pipe character. Say you wanted to amass a collection of the distances of all the tees on the course:

  #{course.teeSets.{ts|ts.tees.{t|t.distance}}}

To get access to all the tees, instead of the tee distances, you have to reference the iterator variable in the expression segment, highlighted in bold:

  #{course.teeSets.{ts|ts.tees.{t|t}}}

Projections are convenient if you’re trying to save typing and perform quick one-liners like you can do in languages such as Ruby and Groovy. Projections demonstrate how Seam stretches the limits of Java EE to offer next-generation efficiencies. Even if you don’t take advantage of projections, you’ll likely use the other EL enhancements quite often.

The final place you’ll access Seam components is in annotations, which you’ll get a heavy dose of in chapter 6. You should now have an appreciation for the various ways in which you can interact with Seam’s contextual container for the purpose of retrieving component instances. Trust that you’ll get plenty more practice at it as you progress through the book.

4.8. Summary

This chapter covered the two essential concepts in Seam: components and contexts. The chapter opened by introducing Seam’s rich contexts, which provides a centralized storage mechanism for objects. You learned that objects stored in these buckets are known as context variables. Seam’s contexts build on those available in the Java Servlet API, encouraging you to hold long-running state to support use cases without fear of memory leaks, expensive replication, or concurrency problems.

The term component was given meaning in the context of Seam, being defined as a blueprint for creating an object whose lifecycle is managed by Seam. You learned that creation happens when the name assigned to a component is requested and an instance doesn’t already exist. In this sense, the Seam container is a simple object factory. But you discovered that Seam goes well beyond instantiating the component by wrapping it with method interceptors that allow Seam to weave functionality into the instance, provide life-cycle callbacks, and ultimately manage it throughout its lifetime. Many of the core interceptors are covered in chapter 6, and additional ones pertaining to conversations persistence, and security are explored in chapters 7,9, and 11, respectively.

Throughout the chapter, examples were provided that demonstrate how to access components using the Seam API and EL notation. These examples serve merely as a warm-up for what’s to come. Indeed, components are used in every remaining chapter in this book. If by some chance you don’t feel comfortable with how to access them yet, you’ll get plenty more practice, especially with how they’re exchanged declaratively using annotations in chapter 6.

To balance the focus on annotations in this chapter, the next chapter presents an XML-based approach to defining components. You discover that this alternative approach is about more than just replacing @ symbols with angled brackets. You learn that XML gives you a way to configure components by assigning initial values. Component configuration, as this mechanism is termed, builds on the basic component knowledge that you learned about in this chapter. You have only seen the beginning of what you can do with a component.

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

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