SeamFramework.orgCommunity Documentation
In this tutorial, we'll assume that you have downloaded JBoss AS 4.2. You should also have a copy of Seam downloaded and extracted to a work directory.
The directory structure of each example in Seam follows this pattern:
Web pages, images and stylesheets may be found in
examples/
registration/view
Resources such as deployment descriptors and data import scripts may be found in
examples/
registration/resources
Java source code may be found in
examples/
registration/src
The Ant build script is
examples/
registration/build.xml
First, make sure you have Ant correctly installed, with $ANT_HOME and
$JAVA_HOME set correctly. Next, make sure you set the location of your JBoss AS
4.2 installation in the build.properties file in the root folder of your Seam
installation. If you haven't already done so, start JBoss AS now by typing bin/run.sh
or bin/run.bat in the root directory of your JBoss installation.
Now, build and deploy the example by typing ant deploy in the
examples/ directory. registration
Try it out by accessing
http://localhost:8080/seam-registration/
with your web browser.
First, make sure you have Ant correctly installed, with $ANT_HOME and
$JAVA_HOME set correctly. Next, make sure you set the location of your Tomcat
6.0 installation in the build.properties file in the root folder of your Seam
installation. You will need to follow the instructions in Section 26.6.1, “Installing Embedded JBoss”
for installing JBoss Embedded on Tomcat 6.0. JBoss Embedded is required to run the Seam demo
applications on Tomcat. (However, it is possible to use Seam on Tomcat without JBoss Embedded.)
Now, build and deploy the example by typing ant tomcat.deploy in the
examples/ directory.
registration
Finally, start Tomcat.
Try it out by accessing
http://localhost:8080/jboss-seam-registration/
with your web browser.
When you deploy the example to Tomcat, any EJB3 components will run inside the JBoss Embeddable EJB3 container, a complete standalone EJB3 container environment.
The registration example is a fairly trivial application that lets a new user store his username, real name and password in the database. The example isn't intended to show off all of the cool functionality of Seam. However, it demonstrates the use of an EJB3 session bean as a JSF action listener, and basic configuration of Seam.
We'll go slowly, since we realize you might not yet be familiar with EJB 3.0.
The start page displays a very basic form with three input fields. Try filling them in and then submitting the form. This will save a user object in the database.

The example is implemented with two JSP pages, one entity bean and one stateless session bean.

Let's take a look at the code, starting from the "bottom".
We need an EJB entity bean for user data. This class defines persistence and validation declaratively, via annotations. It also needs some extra annotations that define the class as a Seam component.
Example 1.1.
@Entity <co id="registration-entity-annotation"/>@Name("user")
@Scope(SESSION)
@Table(name="users")
public class User implements Serializable
{
private static final long serialVersionUID = 1881413500711441951L;
private String username;
private String password;
private String name;
public User(String name, String password, String username)
{
this.name = name;
this.password = password;
this.username = username;
}
public User() {}
@NotNull @Length(min=5, max=15)
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
@NotNull
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
@Id @NotNull @Length(min=5, max=15)
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
}
| ??? | The EJB3 standard |
![]() | A Seam component needs a component name specified by the
|
![]() | Whenever Seam instantiates a component, it binds the new instance to a context
variable in the component's default context. The default
context is specified using the
|
![]() | The EJB standard |
![]() |
|
![]() | An empty constructor is both required by both the EJB specification and by Seam. |
![]() | The |
![]() | The EJB standard |
The most important things to notice in this example are the @Name and
@Scope annotations. These annotations establish that this class is a Seam component.
We'll see below that the properties of our User class are bound
directly to JSF components and are populated by JSF during the update model values phase. We
don't need any tedious glue code to copy data back and forth between the JSP pages and the
entity bean domain model.
However, entity beans shouldn't do transaction management or database access. So we can't use this component as a JSF action listener. For that we need a session bean.
Most Seam application use session beans as JSF action listeners (you can use JavaBeans instead if you like).
We have exactly one JSF action in our application, and one session bean method attached to it. In
this case, we'll use a stateless session bean, since all the state associated with our action is
held by the User bean.
This is the only really interesting code in the example!
Example 1.2.
@Stateless@Name("register") public class RegisterAction implements Register { @In
private User user; @Persistenc
eContext private EntityManager em; @Logger
private Log log; public Stri
ng register() { List existing = em.createQuery( "sele
ct username from User where username=#{user.username}") .getResultList(); if (existing.size()==0) { em.persist(user); log.i
nfo("Registered new user #{user.username}"); retur
n "/registered.xhtml"; } else { Faces
Messages.instance().add("User #{user.username} already exists"); return null; } } }
![]() | The EJB standard |
![]() | The
|
![]() | The EJB standard |
![]() | The Seam |
![]() | The action listener method uses the standard EJB3
|
![]() | Notice that Seam lets you use a JSF EL expression inside EJB-QL. Under the
covers, this results in an ordinary JPA |
![]() | The |
![]() | JSF action listener methods return a string-valued outcome that determines what page will be displayed next. A null outcome (or a void action listener method) redisplays the previous page. In plain JSF, it is normal to always use a JSF navigation rule to determine the JSF view id from the outcome. For complex application this indirection is useful and a good practice. However, for very simple examples like this one, Seam lets you use the JSF view id as the outcome, eliminating the requirement for a navigation rule. Note that when you use a view id as an outcome, Seam always performs a browser redirect. |
![]() | Seam provides a number of built-in components to help solve
common problems. The |
Note that we did not explicitly specify a @Scope this time. Each Seam
component type has a default scope if not explicitly specified. For stateless session beans, the
default scope is the stateless context. Actually, all stateless session
beans belong in the stateless context.
Our session bean action listener performs the business and persistence logic for our mini-application. In more complex applications, we might need to layer the code and refactor persistence logic into a dedicated data access component. That's perfectly trivial to do. But notice that Seam does not force you into any particular strategy for application layering.
Furthermore, notice that our session bean has simultaneous access to context associated with
the web request (the form values in the User object, for example), and state
held in transactional resources (the EntityManager object). This is a break
from traditional J2EE architectures. Again, if you are more comfortable with the traditional
J2EE layering, you can certainly implement that in a Seam application. But for many
applications, it's simply not very useful.
Naturally, our session bean needs a local interface.
That's the end of the Java code. Now onto the deployment descriptors.
If you've used many Java frameworks before, you'll be used to having to declare all your component classes in some kind of XML file that gradually grows more and more unmanageable as your project matures. You'll be relieved to know that Seam does not require that application components be accompanied by XML. Most Seam applications require a very small amount of XML that does not grow very much as the project gets bigger.
Nevertheless, it is often useful to be able to provide for some external
configuration of some components (particularly the components built in to
Seam). You have a couple of options here, but the most flexible option is to provide this
configuration in a file called components.xml, located in the
WEB-INF directory. We'll use the components.xml file to tell
Seam how to find our EJB components in JNDI:
Example 1.4.
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
xmlns:core="http://jboss.com/products/seam/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.1.xsd
http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.1.xsd">
<core:init jndi-pattern="@jndiPattern@"/>
</components>
This code configures a property named jndiPattern of a built-in Seam component
named org.jboss.seam.core.init. The funny @ symbols are
there because our Ant build script puts the correct JNDI pattern in when we deploy the application.
The presentation layer for our mini-application will be deployed in a WAR. So we'll need a web deployment descriptor.
Example 1.5.
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
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/web-app_2_5.xsd">
<!-- Seam -->
<listener>
<listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
</listener>
<!-- JSF -->
<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.seam</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>10</session-timeout>
</session-config>
</web-app>
This web.xml file configures Seam and JSF. The configuration you see here is
pretty much identical in all Seam applications.
Most Seam applications use JSF views as the presentation layer. So usually we'll need
faces-config.xml. In our case, we are going to use Facelets for
defining our views, so we need to tell JSF to use Facelets as its templating engine.
Example 1.6.
<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2"
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/web-facesconfig_1_2.xsd">
<!-- Facelets support -->
<application>
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
</application>
</faces-config>
Note that we don't need
any JSF managed bean declarations! Our managed beans are annotated Seam components. In Seam applications,
the faces-config.xml is used much less often than in plain JSF.
In fact, once you have all the basic descriptors set up, the only XML you need to write as you add new functionality to a Seam application is orchestration: navigation rules or jBPM process definitions. Seam takes the view that process flow and configuration data are the only things that truly belong in XML.
In this simple example, we don't even need a navigation rule, since we decided to embed the view id in our action code.
The ejb-jar.xml file integrates Seam with EJB3, by attaching the
SeamInterceptor to all session beans in the archive.
<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>
The persistence.xml file tells the EJB persistence provider where to find the
datasource, and contains some vendor-specific settings. In this case, enables automatic schema
export at startup time.
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="userDatabase">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
</properties>
</persistence-unit>
</persistence>
The view pages for a Seam application could be implemented using any technology that supports JSF. In this example we use Facelets, because we think it's better than JSP.
Example 1.7.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<head>
<title>Register New User</title>
</head>
<body>
<f:view>
<h:form>
<s:validateAll>
<h:panelGrid columns="2">
Username: <h:inputText value="#{user.username}" required="true"/>
Real Name: <h:inputText value="#{user.name}" required="true"/>
Password: <h:inputSecret value="#{user.password}" required="true"/>
</h:panelGrid>
</s:validateAll>
<h:messages/>
<h:commandButton value="Register" action="#{register.register}"/>
</h:form>
</f:view>
</body>
</html>
The only thing here that is specific to Seam is the
<s:validateAll> tag. This JSF component tells JSF to validate all
the contained input fields against the Hibernate Validator annotations specified on the entity bean.
Example 1.8.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core">
<head>
<title>Successfully Registered New User</title>
</head>
<body>
<f:view>
Welcome, #{user.name}, you are successfully registered as #{user.username}.
</f:view>
</body>
</html>
This is a boring old Facelets page using some embedded EL. There is nothing specific to Seam here.
Finally, since our application is deployed as an EAR, we need a deployment descriptor there, too.
Example 1.9. registration application
<?xml version="1.0" encoding="UTF-8"?>
<application 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/application_5.xsd"
version="5">
<display-name>Seam Registration</display-name>
<module>
<web>
<web-uri>jboss-seam-registration.war</web-uri>
<context-root>/seam-registration</context-root>
</web>
</module>
<module>
<ejb>jboss-seam-registration.jar</ejb>
</module>
<module>
<ejb>jboss-seam.jar</ejb>
</module>
<module>
<java>jboss-el.jar</java>
</module>
</application>
This deployment descriptor links modules in the enterprise archive and binds the web application
to the context root /seam-registration.
We've now seen all the files in the entire application!
When the form is submitted, JSF asks Seam to resolve the variable named user.
Since there is no value already bound to that name (in any Seam context), Seam instantiates the
user component, and returns the resulting User entity bean
instance to JSF after storing it in the Seam session context.
The form input values are now validated against the Hibernate Validator constraints specified on the
User entity. If the constraints are violated, JSF redisplays the page. Otherwise,
JSF binds the form input values to properties of the User entity bean.
Next, JSF asks Seam to resolve the variable named register. Seam finds the
RegisterAction stateless session bean in the stateless context and returns it.
JSF invokes the register() action listener method.
Seam intercepts the method call and injects the User entity from the Seam session
context, before continuing the invocation.
The register() method checks if a user with the entered username already exists.
If so, an error message is queued with the FacesMessages component, and a null
outcome is returned, causing a page redisplay. The FacesMessages component
interpolates the JSF expression embedded in the message string and adds a JSF
FacesMessage to the view.
If no user with that username exists, the "/registered.xhtml" outcome triggers a
browser redirect to the registered.xhtml page. When JSF comes to render the page, it
asks Seam to resolve the variable named user and uses property values of the returned
User entity from Seam's session scope.
Clickable lists of database search results are such an important part of any online application that Seam
provides special functionality on top of JSF to make it easier to query data using EJB-QL or HQL and display
it as a clickable list using a JSF <h:dataTable>. The messages example
demonstrates this functionality.

The message list example has one entity bean, Message, one session bean,
MessageListBean and one JSP.
The Message entity defines the title, text, date and time of a message, and a
flag indicating whether the message has been read:
Example 1.10.
@Entity
@Name("message")
@Scope(EVENT)
public class Message implements Serializable
{
private Long id;
private String title;
private String text;
private boolean read;
private Date datetime;
@Id @GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@NotNull @Length(max=100)
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@NotNull @Lob
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
@NotNull
public boolean isRead() {
return read;
}
public void setRead(boolean read) {
this.read = read;
}
@NotNull
@Basic @Temporal(TemporalType.TIMESTAMP)
public Date getDatetime() {
return datetime;
}
public void setDatetime(Date datetime) {
this.datetime = datetime;
}
}
Just like in the previous example, we have a session bean, MessageManagerBean,
which defines the action listener methods for the two buttons on our form. One of the buttons
selects a message from the list, and displays that message. The other button deletes a message. So
far, this is not so different to the previous example.
But MessageManagerBean is also responsible for fetching the list of messages
the first time we navigate to the message list page. There are various ways the user could navigate
to the page, and not all of them are preceded by a JSF action—the user might have
bookmarked the page, for example. So the job of fetching the message list takes place in a Seam
factory method, instead of in an action listener method.
We want to cache the list of messages in memory between server requests, so we will make this a stateful session bean.
Example 1.11.
@Stateful
@Scope(SESSION)
@Name("messageManager")
public class MessageManagerBean implements Serializable, MessageManager
{
@DataModel
private List<Message> messageList;
@DataModelS
election
@Out(requir
ed=false)
private Message message;
@Persistenc
eContext(type=EXTENDED)
private EntityManager em;
@Factory("m
essageList")
public void findMessages()
{
messageList = em.createQuery("from Message msg order by msg.datetime desc")
.getResultList();
}
public void
select()
{
message.setRead(true);
}
public void
delete()
{
messageList.remove(message);
em.remove(message);
message=null;
}
@Remove
public void destroy() {}
}![]() | The |
![]() | The |
![]() | The |
![]() | This stateful bean has an EJB3 extended persistence context.
The messages retrieved in the query remain in the managed state as long as the bean
exists, so any subsequent method calls to the stateful bean can update them without
needing to make any explicit call to the |
![]() | The first time we navigate to the JSP page, there will be no value in the
|
![]() | The |
![]() | The |
![]() | All stateful session bean Seam components must have a method
with no parameters marked |
Note that this is a session-scoped Seam component. It is associated with the user login session, and all requests from a login session share the same instance of the component. (In Seam applications, we usually use session-scoped components sparingly.)
All session beans have a business interface, of course.
@Local
public interface MessageManager
{
public void findMessages();
public void select();
public void delete();
public void destroy();
}
From now on, we won't show local interfaces in our code examples.
Let's skip over components.xml, persistence.xml,
web.xml, ejb-jar.xml, faces-config.xml
and application.xml since they are much the same as the previous example, and go
straight to the JSP.
The JSP page is a straightforward use of the JSF <h:dataTable>
component. Again, nothing specific to Seam.
Example 1.12.
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<html>
<head>
<title>Messages</title>
</head>
<body>
<f:view>
<h:form>
<h2>Message List</h2>
<h:outputText value="No messages to display"
rendered="#{messageList.rowCount==0}"/>
<h:dataTable var="msg" value="#{messageList}"
rendered="#{messageList.rowCount>0}">
<h:column>
<f:facet name="header">
<h:outputText value="Read"/>
</f:facet>
<h:selectBooleanCheckbox value="#{msg.read}" disabled="true"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Title"/>
</f:facet>