JBoss.orgCommunity Documentation
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:
Beginning JBoss Seam by Joseph Faisal Nusairat, Apress 2007.
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.