JBoss.orgCommunity Documentation

Chapter 13. Using Seam

13.1. Data Model
13.2. JSF Web Pages - index.xhtml and create.xhtml
13.3. Data Access using a Session Bean
13.4. JSF Web Pages - todos.xhtml and edit.xhtml
13.5. Xml Files
13.6. Further Information

JBoss Seam is a framework that provides the glue between the new EJB3 and JSF frameworks that are part of the Java EE 5.0 standard. In fact, the name Seam refers to the seamless manner in which it enables developers to use these two frameworks in an integrated manner. Seam automates many of the common tasks, and makes extensive use of annotations to reduce the amount of xml code that needs to be written. The overall effect is to significantly reduce the total amount of coding that needs to be done.

If you are new to Seam, you can find more introductory information from the following url and book:

We have included two versions of the example application, one coded using EJB3 / JSF without using Seam, and one using Seam, to demonstrate clearly the difference in application development using the Seam framework.

Let's start off our examination of the Seam implementation in the same way, by examining how the Data Model is implemented. This is done in the Todo.java file.

@Entity
@Name("todo")
public class Todo implements Serializable {

  private long id;
  private String title;
  private String description;

  public Todo () {
    title ="";
    description ="";
  }

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

  @NotNull
  public String getTitle() { return title; }
  public void setTitle(String title) {this.title = title;}

  @NotNull
  @Length(max=250) 
  public String getDescription() { return description; }
  public void setDescription(String description) {
    this.description = description;
  }

}

The @Entity annotation defines the class as an EJB3 entity bean, and tells the container to map the Todo class to a relational database table. Each property of the class will become a column in the table. Each instance of the class will become a row in this table. Since we have not used the @Table annotation, Seam's "configuration by exception" default will name the table after the class.

@Entity and @Table are both EJB3 annotations, and are not specific to Seam. It is possible to use Seam completely with POJOs (Plain Old Java Objects) without any EJB3-specific annotations. However, EJB3 brings a lot of advantages to the table, including container managed security, message-driven components, transaction and component level persistence context, and @PersistenceContext injection, which we will encounter a little further on.

The @Name annotation is specific to Seam, and defines the string name for Seam to use to register the Entity Bean. This will be the default name for the relational database table. Each component in a Seam application must have a unique name. In the other components in the Seam framework, such as JSF web pages and session beans, you can reference the managed Todo bean using this name. If no instance of this class exists when it is referenced from another component, then Seam will instantiate one.

The @Id annotation defines a primary key id field for the component. @GeneratedValue specifies that the server will automatically generate this value for the component when it is saved to the database.

Seam provides support for model-based constraints defined using Hibernate Validator, although Hibernate does not have to be the object persister used. The @NotNull annotation is a validation constraint that requires this property to have a value before the component can be persisted into the database. Using this annotation allows the validation to be enforced by the JSF code at the view level, without having to specify the exact validation constraint in the JSF code.

At this point the only apparent difference between the Seam version and the EJB3/JSF version of the app is the inclusion of the validator annotation @NotNull, and the @Name annotation. However, while the EJB3/JSF version of this application requires a further TodoBean class to be manually coded and managed in order to handle the interaction between the Todo class and the web interface, when using Seam the Seam framework takes care of this work for us. We'll see how this is done in practice as we examine the implementation of the user interface.

The index.xhtml file used is the same as in the EJB3/JSF example.

create.xhtml begins to reveal the difference that coding using the Seam framework makes.

<h:form id="create">

<f:facet name="beforeInvalidField">
  <h:graphicImage styleClass="errorImg" value="error.png"/>
</f:facet>
<f:facet name="afterInvalidField">
  <s:message styleClass="errorMsg" />
</f:facet>
<f:facet name="aroundInvalidField">
  <s:div styleClass="error"/>
</f:facet>

<s:validateAll>

<table>

  <tr>
    <td>Title:</td>
    <td>
      <s:decorate>
        <h:inputText id="title" value="#{todo.title}" size="15"/>
      </s:decorate>
    </td>
  </tr>

  <tr>
    <td>Description:</td>
    <td>
      <s:decorate>
        <h:inputTextarea id="description" value="#{todo.description}"/>
      </s:decorate>
    </td>
  </tr>

</table>

</s:validateAll>

<h:commandButton type="submit" id="create" value="Create"
                 action="#{todoDao.persist}"/>
</h:form>
		

The first thing that is different here is the Java Server Facelet code at the beginning, which works with the @NotNull validation constraint of our todo class to enforce and indicate invalid input to the user.

Also notice here that rather than requiring the use of a TodoBean class as we did in the EJB3/JSF example we back the form directly with a Todo entity bean. When this page is called, JSF asks Seam to resolve the variable todo due to JSF EL references such as #{todo.title}. Since there is no value already bound to that variable name, Seam will instantiate an entity bean of the todo class and return it to JSF, after storing it in the Seam context. The Seam context replaces the need for an intermediary bean.

The form input values are validated against the Hibernate Validator constraints specified in the todo class. JSF will redisplay the page if the constraints are violated, or it will bind the form input values to the Todo entity bean.

Entity beans shouldn't do database access or transaction management, so we can't use the Todo entity bean as a JSF action listener. Instead, creation of a new todo item in the database is accomplished by calling the persist method of a TodoDao session bean. When JSF requests Seam to resolve the variable todoDao through the JSF EL expression #{todoDao.persist}, Seam will either instantiate an object if one does not already exist, or else pass the existing stateful todoDao object from the Seam context. Seam will intercept the persist method call and inject the todo entity from the session context.

Let's have a look at the TodoDao class (defined in TodoDao.java) to see how this injection capability is implemented.

Let's go through a listing of the code for the TodoDao class.

@Stateful
@Name("todoDao")
public class TodoDao implements TodoDaoInt {

  @In (required=false) @Out (required=false)
  private Todo todo;

  @PersistenceContext (type=EXTENDED)
  private EntityManager em;

  // Injected from pages.xml
  Long id;
 
  public String persist () {
    em.persist (todo);
    return "persisted";
  }

  @DataModel
  private List <Todo> todos;

  @Factory("todos")
  public void findTodos () {
    todos = em.createQuery("select t from Todo t")
                                  .getResultList();
  }

  public void setId (Long id) {
    this.id = id;
    
    if (id != null) {
      todo = (Todo) em.find(Todo.class, id);
    } else {
      todo = new Todo ();
    }
  }
  
  public Long getId () {
    return id;
  }

  public String delete () {
    em.remove( todo );
    return "removed";
  }

  public String update () {
    return "updated";
  }

  @Remove @Destroy
  public void destroy() {}

}

First of all notice that this is a stateful session bean. Seam can use both stateful and stateless session beans, the two most common types of EJB3 beans.

The @In and @Out annotations define an attribute that is injected by Seam. The attribute is injected to this object or from this object to another via a Seam context variable named todo, a reference to the Seam registered name of our Todo class defined in Todo.java.

The @PersistenceContext annotation injects the EJB3 Entity manager, allowing this object to persist objects to the database. Because this is a stateful session bean and the PersistenceContext type is set to EXTENDED, the same Entity Manager instance is used until the Remove method of the session bean is called. The database to be used (a persistence-unit) is defined in the file resources/META-INF/persistence.xml

Note that this session bean has simultaneous access to context associated with web request (the form values of the todo object), and state held in transactional resources (the EntityManager). This is a break from traditional J2EE architectures, but Seam does not force you to work this way. You can use more traditional forms of application layering if you wish.

The @DataModel annotation initializes the todos property, which will be outjected or "exposed" to the view. The @Factory annotated method performs the work of generating the todos list, and is called by Seam if it attempts to access the exposed DataModel property and finds it to be null. Notice the absence of property access methods for the todos property. Seam takes care of this for you automatically.

Let's take a look at the JSF code that we use for displaying and editing the list of todos, to get an idea of how to use these interfaces in practice.

Using the DataModel exposed property of the Session Bean it becomes trivial to produce a list of todos:

<h:form>

<h:dataTable value="#{todos}" var="todo">
  <h:column>
    <f:facet name="header">Title</f:facet>
    #{todo.title}
  </h:column>
  <h:column>
    <f:facet name="header">Description</f:facet>
    #{todo.description}
  </h:column>
  <h:column>
    <a href="edit.seam?tid=#{todo.id}">Edit</a>
  </h:column>
</h:dataTable>

<center>
  <h:commandButton action="create"
            value="Create New Todo" type="submit"/>
</center>

</h:form>

When the JSF variable resolver encounters {#todos} and requests todos, Seam finds that there is no "todos" component in the current scope, so it calls the @Factory("todos") method to make one. The todos object is then outjected once the factory method is done since it is annotated with the @DataModel annotation.

Constructing the view for the edit page is similarly straight forward:

<h:form id="edit">
  
<f:facet name="beforeInvalidField">
  <h:graphicImage styleClass="errorImg" value="error.png"/>
</f:facet>
<f:facet name="afterInvalidField">
  <s:message styleClass="errorMsg" />
</f:facet>
<f:facet name="aroundInvalidField">
  <s:div styleClass="error"/>
</f:facet>

<s:validateAll>

<table>

  <tr>
    <td>Title:</td>
    <td>
      <s:decorate>
        <h:inputText id="title" value="#{todo.title}" size="15"/>
      </s:decorate>
    </td>
  </tr>

  <tr>
    <td>Description:</td>
    <td>
      <s:decorate>
        <h:inputTextarea id="description" value="#{todo.description}"/>
      </s:decorate>
    </td>
  </tr>

</table>

</s:validateAll>

<h:commandButton type="submit" id="update" value="Update"
                 action="#{todoDao.update}"/>

<h:commandButton type="submit" id="delete" value="Delete"
                 action="#{todoDao.delete}"/>
</h:form>
		

Here we see the same factors in play. JSF validation code taking advantage of the validation constraints defined in our Entity Bean, and the use of the todoDao Session Bean's update and delete methods to update the database.

The call from todos.xhtml: edit.seam?tid=#{todo.id} causes Seam to create a todoDao and set it's id property to tid. Setting its id property causes the todoDao to retrieve the appropriate record from the database.

The functionality that allows the edit page to be called with a parameter in this way is implemented through pages.xml. Let's have a look at the pages.xml file and how it is used by Seam applications.

Seam drastically reduces the amount of xml coding that needs to be done. One file that is of interest is the pages.xml, packaged in the app.war file's WEB-INF directory. This file is available in the resources/WEB-INF directory in the source code bundle. The pages.xml file is used to define page descriptions including Seam page parameters (HTTP GET parameters), page actions, page navigation rules, error pages etc. Among other things it can be used in a Seam application to define exception handlers and redirections.

In the case of our sample application we are using it to define a Seam page parameter. The pages.xml in this example contains the following code:

<page view-id="/edit.xhtml">
    <param name="tid" value="#{todoDao.id}" 
           converterId="javax.faces.Long"/>
</page>

This defines a parameter named tid for the edit.xhtml page. When the edit.xhtml page is loaded, the HTTP GET request parameter tid is converted to a Long value and assigned to the id property of the todoDao object. You can have as many page parameters as required to bind HTTP GET request parameters to the back-end components in your application.

This completes our walkthrough of the sample Seam application. For further, detailed information on developing applications using the Seam framework, please refer to the The Seam Reference Guide.