3.2. Your first Seam application: the registration example

The registration example is a simple application that allows a new user to store user name, real name, and password in the database. This example uses only basic functions to demonstrate the use of an EJB3 session bean as a JSF action listener, and the basic configuration of Seam.
The start page displays a basic form with three input fields. If a user fills these fields and submits the form a user object is saved in the database.

3.2.1. Understanding the code

The example is implemented with two Facelet templates: entity bean, and stateless session bean. This section explains the code in detail, starting from the base level.

3.2.1.1. The entity bean: User.java

You need an EJB entity bean for user data. This class defines persistence and validation declaratively through annotations. It also requires some extra annotations to define the class as a Seam component.

Example 3.1. User.java

@Entity                                                                  1
@Name("user")                                                            2
@Scope(SESSION)                                                          3
@Table(name="users")                                                     4
public class User implements Serializable
{
   private static final long serialVersionUID = 1881413500711441951L;
   
   private String username;
   private String password;                                              5
   private String name;
   
   public User(String name, String password, String username)
   {
      this.name = name;
      this.password = password;
      this.username = username;
   }
   
   public User() {}
   
   @NotNull @Size(min=5, max=15)
   public String getPassword()                                           6
   {
      return password;                                                   7
   }

   public void setPassword(String password)
   {
      this.password = password;
   }
   
   @NotNull
   public String getName()
   {
      return name;
   }

   public void setName(String name)
   {
      this.name = name;
   }
   
   @Id @NotNull @Size(min=5, max=15)
   public String getUsername()
   {
      return username;                                                   8
   }

   public void setUsername(String username)
   {
      this.username = username;
   }

}

1

The EJB3 standard @Entity annotation indicates that the User class is an entity bean.

2

A Seam component needs a component name specified by the @Name annotation. This name must be unique within the Seam application. When JSF asks Seam to resolve a context variable with a name that is the same as a Seam component name, and the context variable is currently undefined (null), Seam instantiates the component, and binds the new instance to the context variable. In this case, Seam instantiates a User the first time JSF encounters a variable named user.

3

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 @Scope annotation. The User bean is a session scoped component.

4

The EJB standard @Table annotation indicates that the User class is mapped to the users table.

5

name, password, and username are persistent attributes of the entity bean. All persistent attributes define accessor methods. These attributes are required when a component is used by JSF in the render response, and update model values phase.

6

An empty constructor is required by both; EJB specification, and Seam.

7

The @NotNull and @Size annotations are part of the Hibernate Validator framework. Seam integrates Hibernate Validator and allows you use it for data validation (even if you are not using Hibernate for persistence).

8

The EJB standard @Id annotation indicates the primary key attribute of the entity bean.
In the above example, @Name and @Scope annotations are very important as these annotations establish the class as a Seam component.
The next section shows that the properties of the User class are bound directly to JSF components and populated by JSF during the update model values phase. There is no glue code to copy data back and forth between the JSP pages and the entity bean domain model.
However, entity beans do not perform transaction management or database access. So, the JSF component is not used as a JSF action listener. In this situation, use a session bean.

3.2.1.2. The stateless session bean class: RegisterAction.java

Most Seam applications use session beans as JSF action listeners, though you may also use JavaBeans.
This example application has exactly one JSF action and one session bean method attached to it. It uses a stateless session bean as the state associated with the action is retained by the User bean.
The relevant code is shown below:

Example 3.2. RegisterAction.java

@Stateless                                                                               1
@Name("register")
public class RegisterAction implements Register
{
    @In                                                                                  2
    private User user;
            
    @PersistenceContext                                                                  3
    private EntityManager em;
            
    @Logger                                                                              4
    private Log log;
            
    public String register()                                                             5
    {
        List existing = em.createQuery("select username " +
                                       "from User " +
                                       "where username = #{user.username}")              6
            .getResultList();
            
        if (existing.size()==0)
            {
                em.persist(user);
                log.info("Registered new user #{user.username}");                        7
                return "/registered.xhtml";                                              8
            }
        else
            {
                FacesMessages.instance().add("User #{user.username} already exists");    9
                return null;
            }
    }

}

1

The EJB @Stateless annotation marks this class as a stateless session bean.

2

The @In annotation marks an attribute of the bean as injected by Seam. In this case, the attribute is injected from a context variable named user (the instance variable name).

3

The EJB standard @PersistenceContext annotation is used to inject the EJB3 entity manager.

4

The Seam @Logger annotation is used to inject the component's Log instance.

5

The action listener method uses the standard EJB3 EntityManager API to interact with the database, and returns the JSF outcome. Note that, as this is a session bean, a transaction automatically starts when the register() method is called, and committed when the register() method completes.

6

Seam allows you to use a JSF EL expression inside EJB-QL. This results in an ordinary JPA setParameter() call on the standard JPA Query object.

7

The Log API allows you to easily display templated log messages that can also use JSF EL expressions.

8

JSF action listener methods return a string-valued outcome that determines the page that is 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 applications this indirection is useful and a good practice. However, for very simple examples like this one, Seam allows you to 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.

9

Seam provides a number of built-in components to help solve common problems. The FacesMessages component makes it easy to display templated error or success messages. (As of Seam 2.1, you can use StatusMessages instead to remove the semantic dependency on JSF). Built-in Seam components may be obtained by injection, or by calling the instance() method on the class of the built-in component.
Note that we did not explicitly specify a @Scope this time. Each Seam component type has a default scope, which is used if a scope is not explicitly specified. For stateless session beans, the default scope is the stateless context.
The session bean action listener performs the business and persistence logic for a mini-application. In a more complex application, a separate service layer might be necessary, but Seam allows you to implement your own strategies for application layering. You can make any application as simple, or as complex, as you want.

Note

This application is more complex than necessary for the sake of clear example code. All the application code could have been eliminated by using Seam's application framework controllers.

3.2.1.3. The session bean local interface: Register.java

The session bean requires a local interface.

Example 3.3. Register.java

@Local
public interface Register
{
     public String register();
}
That is the end of the Java code. The next level to examine is the view.

3.2.1.4. The view: register.xhtml and registered.xhtml

The view pages for a Seam application can be implemented using any technology that supports JSF. The following example is written with Facelets.

Example 3.4. register.xhtml

<?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.org/schema/seam/taglib"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core">

<h:head>
  <title>Register New User</title>
</h:head>
<h: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/>
        <s:button value="Register" action="#{register.register}"/>
    </h:form>
  </f:view>
</h:body>
</html>
The only Seam-specific tag in the above example is <s:validateAll>. This JSF component tells JSF to validate all the contained input fields against the Hibernate Validator annotations specified on the entity bean.

Example 3.5. registered.xhtml

<?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>
The above is a simple Facelets page, created with inline EL — it contains nothing specific to Seam.

3.2.1.5. The Seam component deployment descriptor: components.xml

Seam strongly values minimal configuration. The configuration files are created when you create a Seam application, and they are rarely required to be altered.
Unlike Java frameworks, Seam does not require application components to be accompanied by XML files. Most Seam applications require very few XML files, which do not tend to increase in size as the project expands.
However, it is useful to provide external configuration of some components, particularly the components that are built into Seam. The most flexible option, here, is to provide this configuration in a file called components.xml, located in the WEB-INF directory. The components.xml file can be used to tell Seam the method of finding EJB components in JNDI.

Example 3.6. components.xml

<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.org/schema/seam/components"
    xmlns:core="http://jboss.org/schema/seam/core"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://jboss.org/schema/seam/core
        http://jboss.org/schema/seam/core-2.3.xsd 
        http://jboss.org/schema/seam/components
        http://jboss.org/schema/seam/components-2.3.xsd">
            
    <core:init jndi-pattern="${jndiPattern}"/>
     
</components>
The above code configures jndiPattern property of the built-in Seam component org.jboss.seam.core.init. More information about the working of this process is available at: Section 7.2, “Configuring components via components.xml.

3.2.1.6. The web deployment description: web.xml

The presentation layer of a mini-application is deployed in a WAR, so a web deployment descriptor is required.

Example 3.7. web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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_3_0.xsd"
    version="3.0">

    <listener>
        <listener-class>org.jboss.seam.servlet.SeamListener</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>
The above web.xml file configures both Seam and JSF. This configuration changes very little between Seam applications.

3.2.1.7. The JSF configuration: faces-config.xml

Most Seam applications use JSF views as the presentation layer, so faces-config.xml is usually a requirement.

Example 3.8. faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config 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_2_1.xsd"
              version="2.1">
</faces-config>
Note that JSF managed bean declarations are unnecessary because the managed beans are annotated Seam components. In Seam applications, faces-config.xml file is used less often than in plain JSF.
After setting the basic descriptors, define the orchestration: navigation rules to add functionality to the Seam application. Seam operates on the principle that process flow and configuration data are all that truly belongs in XML.
The above example does not require a navigation rule, as the view ID is embedded in the action code.

3.2.1.8. The EJB deployment descriptor: ejb-jar.xml

The ejb-jar.xml file integrates Seam with EJB3 by attaching the SeamInterceptor to all the session beans in the archive.

Example 3.9. ejb-jar.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>

3.2.1.9. The JPA persistence deployment descriptor: persistence.xml

The persistence.xml file contains the location of the datasource that is required by the JPA persistence provider, and also contains some vendor-specific settings. In this case, the persistence.xml file also enables automatic schema export at startup.

Example 3.10. persistence.xml

<?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_2_0.xsd" 
    version="2.0">

    <persistence-unit name="userDatabase">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
        <properties>
            <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
        </properties>
    </persistence-unit>
    
</persistence>

3.2.1.10. The EAR deployment descriptor: application.xml

Applications deployed as an EAR require a deployment descriptor. The descriptor file can be generated by Maven EAR plug-in if the registration application has it set up in the registration-ear/pom.xml file.

Example 3.11. 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_6.xsd"
    version="6">
    <display-name>registration-ear</display-name>
    <module>
        <web>
            <web-uri>registration-web.war</web-uri>
            <context-root>/seam-registration</context-root>
        </web>
    </module>
    <module>
        <ejb>registration-ejb.jar</ejb>
    </module>
    <module>
        <ejb>jboss-seam.jar</ejb>
    </module>
</application>
This deployment descriptor links modules in the enterprise archive and binds the web application to the context root /seam-registration.
You have now seen all the files in the application.

3.2.2. How it works

When the form is submitted, JSF requests Seam to resolve the user variable. The uservariable is not assigned a value yet (in any Seam context). So, Seam instantiates the user component, and returns the resulting User entity bean instance to JSF after storing the instance in the Seam session context.
The form input values are 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 the properties of the User entity bean.
JSF requests Seam to resolve the register variable. Seam uses the JNDI pattern to locate the stateless session bean, wraps the bean as a Seam component, and returns it. Seam then presents this Seam component to JSF, and JSF invokes the register() action listener method.
Seam then intercepts the method call and injects the User entity from the Seam session context, before allowing the invocation to continue.
The register() method checks if a user already exists with the entered username. 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 exists with the entered username, 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.