Seam - Contextual Components

A Framework for Java EE 5

1.0.1.GA


Table of Contents

Introduction to JBoss Seam
1. Seam Tutorial
1.1. Try the examples
1.1.1. Running the examples on JBoss AS
1.1.2. Running the examples on Tomcat
1.1.3. Running the example tests
1.2. Your first Seam application: the registration example
1.2.1. Understanding the code
1.2.1.1. The entity bean: User.java
1.2.1.2. The stateless session bean class: RegisterAction.java
1.2.1.3. The session bean local interface: Register.java
1.2.1.4. The Seam component deployment descriptor: components.xml
1.2.1.5. The web deployment description: web.xml
1.2.1.6. The JSF configration: faces-config.xml
1.2.1.7. The EJB deployment descriptor: ejb-jar.xml
1.2.1.8. The EJB persistence deployment descriptor: persistence.xml
1.2.1.9. The view: register.jsp and registered.jsp
1.2.1.10. The EAR deployment descriptor: application.xml
1.2.2. How it works
1.3. Clickable lists in Seam: the messages example
1.3.1. Understanding the code
1.3.1.1. The entity bean: Message.java
1.3.1.2. The stateful session bean: MessageManagerBean.java
1.3.1.3. The session bean local interface: MessageManager.java
1.3.1.4. The view: messages.jsp
1.3.2. How it works
1.4. Seam and jBPM: the todo list example
1.4.1. Understanding the code
1.4.2. How it works
1.5. Seam pageflow: the numberguess example
1.5.1. Understanding the code
1.5.2. How it works
1.6. A complete Seam application: the Hotel Booking example
1.6.1. Introduction
1.6.2. Overview of the booking example
1.6.3. Understanding Seam conversations
1.6.4. The Seam UI control library
1.6.5. The Seam Debug Page
1.7. A complete application featuring Seam and jBPM: the DVD Store example
1.8. A complete application featuring Seam workspace management: the Issue Tracker example
1.9. An example of Seam with Hibernate: the Hibernate Booking example
1.10. A RESTful Seam application: the Blog example
1.10.1. Using "pull"-style MVC
1.10.2. Bookmarkable search results page
1.10.3. Using "push"-style MVC in a RESTful application
2. The contextual component model
2.1. Seam contexts
2.1.1. Stateless context
2.1.2. Event context
2.1.3. Page context
2.1.4. Conversation context
2.1.5. Session context
2.1.6. Business process context
2.1.7. Application context
2.1.8. Context variables
2.1.9. Context search priority
2.2. Seam components
2.2.1. Stateless session beans
2.2.2. Stateful session beans
2.2.3. Entity beans
2.2.4. JavaBeans
2.2.5. Message-driven beans
2.2.6. Interception
2.2.7. Component names
2.2.8. Defining the component scope
2.2.9. Components with multiple roles
2.2.10. Built-in components
2.3. Configuring components
2.3.1. Configuring components via property settings
2.3.2. Configuring components via components.xml
2.4. Bijection
2.5. Logging
2.6. Seam interceptors
2.7. Seam events
2.7.1. Page actions
2.7.2. Component-driven events
3. Conversations and workspace management
3.1. Seam's conversation model
3.2. Nested conversations
3.3. Starting conversations with GET requests
3.4. Using <s:link>
3.5. Success messages
3.6. Using an "explicit" conversation id
3.7. Workspace management
3.7.1. Workspace management and JSF navigation
3.7.2. Workspace management and jPDL pageflow
3.7.3. The conversation switcher
3.7.4. The conversation list
3.7.5. Breadcrumbs
3.8. Seam and AJAX
3.9. Seam and SOAP
4. Pageflows and business processes
4.1. Pageflow in Seam
4.1.1. The two navigation models
4.1.2. Seam and the back button
4.2. Using jPDL pageflows
4.2.1. Installing pageflows
4.2.2. Starting pageflows
4.2.3. Page nodes and transitions
4.2.4. Controlling the flow
4.2.5. Ending the flow
4.3. Business process management in Seam
4.4. Using jPDL business process definitions
4.4.1. Installing process definitions
4.4.2. Initializing actor ids
4.4.3. Initiating a business brocess
4.4.4. Task assignment
4.4.5. Task lists
4.4.6. Performing a task
5. Internationalization
5.1. Locales
5.2. Labels
5.3. Faces messages
6. Messaging in Seam
6.1. Configuration
6.2. Sending messages
6.3. Receiving messages using a message-driven bean
6.4. Receiving messages in the client
7. Remoting
7.1. Configuration
7.2. The "Seam" object
7.2.1. A Hello World example
7.2.2. Seam.Component
7.2.2.1. Seam.Component.newInstance()
7.2.2.2. Seam.Component.getInstance()
7.2.2.3. Seam.Component.getComponentName()
7.2.3. Seam.Remoting
7.2.3.1. Seam.Remoting.createType()
7.2.3.2. Seam.Remoting.getTypeName()
7.3. Client Interfaces
7.4. The Context
7.4.1. Setting and reading the Conversation ID
7.5. Batch Requests
7.6. Working with Data types
7.6.1. Primitives / Basic Types
7.6.1.1. String
7.6.1.2. Number
7.6.1.3. Boolean
7.6.2. JavaBeans
7.6.3. Dates and Times
7.6.4. Enums
7.6.5. Collections
7.6.5.1. Bags
7.6.5.2. Maps
7.7. Debugging
7.8. The Loading Message
7.8.1. Changing the message
7.8.2. Hiding the loading message
7.8.3. A Custom Loading Indicator
7.9. JMS Messaging
7.9.1. Configuration
7.9.2. Subscribing to a JMS Topic
7.9.3. Unsubscribing from a Topic
7.9.4. Tuning the Polling Process
8. Seam and JBoss Rules
8.1. Installing rules
8.2. Using rules from a Seam component
8.3. Using rules from a jBPM process definition
9. Configuring Seam
9.1. Basic Seam configuration
9.1.1. Integrating Seam with JSF and your servlet container
9.1.2. Integrating Seam with your EJB container
9.1.3. Enabling conversation propagation with redirects
9.2. Configuring Seam in Java EE 5
9.2.1. Packaging
9.3. Configuring Seam with the JBoss Embeddable EJB3 container
9.3.1. Installing the Embeddable EJB3 container
9.3.2. Configuring a datasource with the Embeddable EJB3 container
9.3.3. Packaging
9.4. Seam managed transactions
9.4.1. Enabling Seam-managed transactions
9.4.2. Using a Seam-managed persistence context
9.5. Configuring Seam with Hibernate in Java EE
9.5.1. Boostrapping Hibernate in Seam
9.5.2. Using a Seam-managed Hibernate Session
9.5.3. Packaging
9.6. Configuring Seam with Hibernate in Java SE
9.6.1. Using Hibernate and the JBoss Microcontainer
9.6.2. Packaging
9.7. Configuring jBPM in Seam
9.7.1. Packaging
9.8. Configuring Seam in a Portal
10. Seam annotations
10.1. Annotations for component definition
10.2. Annotations for bijection
10.3. Annotations for component lifecycle methods
10.4. Annotations for context demarcation
10.5. Annotations for transaction demarcation
10.6. Annotations for validation
10.7. Annotations for Seam Remoting
10.8. Annotations for Seam interceptors
10.9. Annotations for use with JSF dataTable
10.10. Meta-annotations for databinding
11. Built-in Seam components
11.1. Context injection components
11.2. Utility components
11.3. Components for internationalization
11.4. Components for controlling conversations
11.5. jBPM-related components
11.6. Security-related components
11.7. JMS-related components
11.8. Infrastructural components
11.9. Special components
12. Testing Seam applications
12.1. Unit testing Seam components
12.2. Integration testing Seam applications
13. Seam tools
13.1. jBPM designer and viewer
13.1.1. Business process designer
13.1.2. Pageflow viewer
13.2. CRUD-application generator
13.2.1. Creating a Hibernate configuration file
13.2.2. Creating a Hibernate Console configuration
13.2.3. Reverse engineering and code generation
13.2.3.1. Code Generation Launcher
13.2.3.2. Exporters
13.2.3.3. Generating and using the code

Introduction to JBoss Seam

Seam is an application framework for Java EE 5. It is inspired by the following principles:

Integrate JSF with EJB 3.0

JSF and EJB 3.0 are two of the best new features of Java EE 5. EJB3 is a brand new component model for server side business and persistence logic. Meanwhile, JSF is a great component model for the presentation tier. Unfortunately, neither component model is able to solve all problems in computing by itself. Indeed, JSF and EJB3 work best used together. But the Java EE 5 specification provides no standard way to integrate the two component models. Fortunately, the creators of both models foresaw this situation and provided standard extension points to allow extension and integration of other solutions.

Seam unifies the component models of JSF and EJB3, eliminating glue code, and letting the developer think about the business problem.

Integrated AJAX-based Remoting

Seam provides a built-in JavaScript remoting layer for EJB3 components. AJAX clients can easily call server-side components and subscribe to JMS topics, without the need for an intermediate action layer.

Integrate Business Process as a First Class Construct

Optionally, Seam integrates transparent business process management via jBPM. You won't believe how easy it is to implement complex workflows using jBPM and Seam.

Seam even allows definition of presentation tier conversation flow by the same means.

JSF provides an incredibly rich event model for the presentation tier. Seam enhances this model by exposing jBPM's business process related events via exactly the same event handling mechanism, providing a uniform event model for Seam's uniform component model.

One Kind of "Stuff"

Seam provides a uniform component model. A Seam component may be stateful, with the state associated to any one of a number of contexts, ranging from the long-running business process to a single web request.

There is no distinction between presentation tier components and business logic components in Seam. It is possible to write Seam applications where "everything" is an EJB. This may come as a surprise if you are used to thinking of EJBs as coarse-grained, heavyweight objects that are a pain in the backside to create! However, EJB 3.0 completely changes the nature of EJB from the point of view of the developer. An EJB is a fine-grained object - nothing more complex than an annotated JavaBean. Seam even encourages you to use session beans as JSF action listeners!

Unlike plain Java EE or J2EE components, Seam components may simultaneously access state associated with the web request and state held in transactional resources (without the need to propagate web request state manually via method parameters). You might object that the application layering imposed upon you by the old J2EE platform was a Good Thing. Well, nothing stops you creating an equivalent layered architecture using Seam - the difference is that you get to architect your own application and decide what the layers are and how they work together.

Declarative State Management

We are all used to the concept of declarative transaction management and J2EE declarative security from EJB 2.x. EJB 3.0 even introduces declarative persistence context management. These are three examples of a broader problem of managing state that is associated with a particular context, while ensuring that all needed cleanup occurs when the context ends. Seam takes the concept of declarative state management much further and applies it to application state. Traditionally, J2EE applications almost always implement state management manually, by getting and setting servlet session and request attributes. This approach to state management is the source of many bugs and memory leaks when applications fail to clean up session attributes, or when session data associated with different workflows collides in a multi-window application. Seam has the potential to almost entirely eliminate this class of bugs.

Declarative application state management is made possible by the richness of the context model defined by Seam. Seam extends the context model defined by the servlet spec—request, session, application—with two new contexts—conversation and business process—that are more meaningful from the point of view of the business logic.

Bijection

The notion of Inversion of Control or dependency injection exists in both JSF and EJB3, as well as in numerous so-called "lighweight containers". Most of these containers emphasize injection of components that implement stateless services. Even when injection of stateful components is supported (such as in JSF), it is virtually useless for handling application state because the scope of the stateful component cannot be defined with sufficient flexibility.

Bijection differs from IoC in that it is dynamic, contextual, and bidirectional. You can think of it as a mechanism for aliasing contextual variables (names in the various contexts bound to the current thread) to attributes of the component. Bijection allows auto-assembly of stateful components by the container. It even allows a component to safely and easily manipulate the value of a context variable, just by assigning to an attribute of the component.

Workspace Management

Optionally, Seam applications may take advantage of workspace management, allowing users to freely switch between different conversations (workspaces) in a single browser window. Seam provides not only correct multi-window operation, but also multi-window-like operation in a single window!

Annotated POJOs Everywhere

EJB 3.0 embraces annotations and "configuration by exception" as the easiest way to provide information to the container in a declarative form. Unfortunately, JSF is still heavily dependent on verbose XML configuration files. Seam extends the annotations provided by EJB 3.0 with a set of annotations for declarative state management and declarative context demarcation. This lets you eliminate the noisy JSF managed bean declarations and reduce the required XML to just that information which truly belongs in XML (the JSF navigation rules).

Testability as a Core Feature

Seam components, being POJOs, are by nature unit testable. But for complex applications, unit testing alone is insufficient. Integration testing has traditionally been a messy and difficult task for Java web applications. Therefore, Seam provides for testability of Seam applications as a core feature of the framework. You can easily write JUnit or TestNG tests that reproduce a whole interaction with a user, exercising all components of the system apart from the view (the JSP or Facelets page). You can run these tests directly inside your IDE, where Seam will automatically deploy EJB components into the JBoss Embeddable EJB3 container.

Get started now!

Seam works in any application server that supports EJB 3.0. You can even use Seam in a servlet container like Tomcat, or in any J2EE application server, by leveraging the new JBoss Embeddable EJB3 container.

However, we realize that not everyone is ready to make the switch to EJB 3.0. So, in the interim, you can use Seam as a framework for applications that use JSF for presentation, Hibernate (or plain JDBC) for persistence and JavaBeans for application logic. Then, when you're ready to make the switch to EJB 3.0, migration will be straightforward.

It turns out that the combination of Seam, JSF and EJB3 is the simplest way to write a complex web application in Java. You won't believe how little code is required!

Chapter 1. Seam Tutorial

1.1. Try the examples

In this tutorial, we'll assume that you have downloaded JBoss AS 4.0.4 and installed the EJB 3.0 profile (using the JBoss AS installer). 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

1.1.1. Running the examples on JBoss AS

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.0.4 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/registration directory.

Try it out by accessing http://localhost:8080/seam-registration/ with your web browser.

1.1.2. Running the examples on Tomcat

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 5.5 installation in the build.properties file in the root folder of your Seam installation.

Now, build and deploy the example by typing ant deploy.tomcat in the examples/registration directory.

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.

1.1.3. Running the example tests

Most of the examples come with a suite of TestNG integration tests. The easiest way to run the tests is to run ant testexample inside the examples/registration directory. It is also possible to run the tests inside your IDE using the TestNG plugin.

1.2. Your first Seam application: the registration example

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.

1.2.1. Understanding the code

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".

1.2.1.1. The entity bean: User.java

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                                                                                  (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;                                                              (5)
   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() {}                                                                      (6)
   
   @NotNull @Length(min=5, max=15)                                                       (7)
   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)                                                   (8)
   public String getUsername()
   {
      return username;
   }

   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 will instantiate that component, and bind the new instance to the context variable. In this case, Seam will instantiate 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 the persistent attributes of the entity bean. All of our persistent attributes define accessor methods. These are needed when this component is used by JSF in the render response and update model values phases.

(6)

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

(7)

The @NotNull and @Length annotations are part of the Hibernate Validator framework. Seam integrates Hibernate Validator and lets 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.

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 to 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.

1.2.1.2. The stateless session bean class: RegisterAction.java

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                                                                               (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=:username")
         .setParameter("username", user.getUsername())
         .getResultList();
         
      if (existing.size()==0)
      {
         em.persist(user);
         log.info("Registered new user #{user.username}");                               (6)
         return "/registered.jsp";                                                       (7)
      }
      else
      {
         FacesMessages.instance().add("User #{user.username} already exists");           (8)
         return null;
      }
   }

}
(1)

The EJB standard @Stateless annotation marks this class as 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, since this is a sesson bean, a transaction is automatically begun when the register() method is called, and committed when it completes.

(6)

The Log API lets us easily display templated log messages.

(7)

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.

(8)

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. Built-in Seam components may be obtained by injection, or by calling an instance() method.

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.

1.2.1.3. The session bean local interface: Register.java

Naturally, our session bean needs a local interface.

Example 1.3. 

@Local
public interface Register
{
   public String register();
}

That's the end of the Java code. Now onto the deployment descriptors.

1.2.1.4. The Seam component deployment descriptor: components.xml

If you've used many Java frameworks before, you'll be used to having to declate 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. 

<components>

    <component name="org.jboss.seam.core.init">
        <!-- JNDI name pattern for JBoss EJB 3.0 -->
        <property name="jndiPattern">#{ejbName}/local</property>
    </component>

</components>

This code configures a property named jndiPattern of a built-in Seam component named org.jboss.seam.core.init.

1.2.1.5. The web deployment description: web.xml

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.4"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">


    <!-- Seam -->

    <listener>
        <listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
    </listener>

    <!-- MyFaces -->

    <listener>
        <listener-class>
            org.apache.myfaces.webapp.StartupServletContextListener
        </listener-class>
    </listener>

    <context-param>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>client</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>

    <!-- Faces Servlet Mapping -->
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.seam</url-pattern>
    </servlet-mapping>

</web-app>

This web.xml file configures Seam and MyFaces. The configuration you see here is pretty much identical in all Seam applications.

1.2.1.6. The JSF configration: faces-config.xml

All Seam applications use JSF views as the presentation layer. So we'll need faces-config.xml.

Example 1.6. 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE faces-config 
PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
                            "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
<faces-config>

    <!-- A phase listener is needed by all Seam applications -->
    
    <lifecycle>
        <phase-listener>org.jboss.seam.jsf.SeamPhaseListener</phase-listener>
    </lifecycle>

</faces-config>

The faces-config.xml file integrates Seam into JSF. Note that we don't need any JSF managed bean declarations! The managed beans are the 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 the navigation rules, and possibly 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.

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

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

<ejb-jar>
   <assembly-descriptor>
      <interceptor-binding>
         <ejb-name>*</ejb-name>
         <interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class>
      </interceptor-binding>
   </assembly-descriptor>
</ejb-jar>

1.2.1.8. The EJB persistence deployment descriptor: persistence.xml

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.

<persistence>
    <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>

1.2.1.9. The view: register.jsp and registered.jsp

The view pages for a Seam application could be implemented using any technology that supports JSF. In this example we use JSP, since it is familiar to most developers and since we have minimal requirements here anyway. (But if you take our advice, you'll use Facelets for your own applications.)

Example 1.7. 

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://jboss.com/products/seam/taglib" prefix="s" %>
<html>
 <head>
  <title>Register New User</title>
 </head>
 <body>
  <f:view>
   <h:form>
     <table border="0">
       <s:validateAll>
         <tr>
           <td>Username</td>
           <td><h:inputText value="#{user.username}"/></td>
         </tr>
         <tr>
           <td>Real Name</td>
           <td><h:inputText value="#{user.name}"/></td>
         </tr>
         <tr>
           <td>Password</td>
           <td><h:inputSecret value="#{user.password}"/></td>
         </tr>
       </s:validateAll>
     </table>
     <h:messages/>
     <h:commandButton type="submit" 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. 

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<html>
 <head>
  <title>Successfully Registered New User</title>
 </head>
 <body>
  <f:view>
    Welcome, <h:outputText value="#{user.name}"/>, 
    you are successfully registered as <h:outputText value="#{user.username}"/>.
  </f:view>
 </body>
</html>

This is a boring old JSP pages using standard JSF components. There is nothing specific to Seam here.

1.2.1.10. The EAR deployment descriptor: application.xml

Finally, since our application is deployed as an EAR, we need a deployment descriptor there, too.

Example 1.9. 

<application>
    <display-name>Seam</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>
        <java>jboss-seam.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!

1.2.2. How it works

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.jsp" outcome triggers a browser redirect to the registered.jsp 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.

1.3. Clickable lists in Seam: the messages example

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.

1.3.1. Understanding the code

The message list example has one entity bean, Message, one session bean, MessageListBean and one JSP.

1.3.1.1. The entity bean: Message.java

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;
   }
   
}

1.3.1.2. The stateful session bean: MessageManagerBean.java

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                                                                            (1)
   private List<Message> messageList;
   
   @DataModelSelection                                                                   (2)
   @Out(required=false)                                                                  (3)
   private Message message;
   
   @PersistenceContext(type=EXTENDED)                                                    (4)
   private EntityManager em;
   
   @Factory("messageList")                                                               (5)
   public void findMessages()
   {
      messageList = em.createQuery("from Message msg order by msg.datetime desc").getResultList();
   }
   
   public void select()                                                                  (6)
   {
      message.setRead(true);
   }
   
   public void delete()                                                                  (7)
   {
      messageList.remove(message);
      em.remove(message);
      message=null;
   }
   
   @Remove @Destory                                                                      (8)
   public void destroy() {}

}
(1)

The @DataModel annotation exposes an attibute of type java.util.List to the JSF page as an instance of javax.faces.model.DataModel. This allows us to use the list in a JSF <h:dataTable> with clickable links for each row. In this case, the DataModel is made available in a session context variable named messageList.

(2)

The @DataModelSelection annotation tells Seam to inject the List element that corresponded to the clicked link.

(3)

The @Out annotation then exposes the selected value directly to the page. So ever time a row of the clickable list is selected, the Message is injected to the attribute of the stateful bean, and the subsequently outjected to the event context variable named message.

(4)

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 EntityManager.

(5)

The first time we navigate to the JSP page, there will be no value in the messageList context variable. The @Factory annotation tells Seam to create an instance of MessageManagerBean and invoke the findMessages() method to initialize the value. We call findMessages() a factory method for messages.

(6)

The select() action listener method marks the selected Message as read, and updates it in the database.

(7)

The delete() action listener method removes the selected Message from the database.

(8)

All stateful session bean Seam components must have a method marked @Remove @Destroy to ensure that Seam will remove the stateful bean when the Seam context ends, and clean up any server-side state.

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.)

1.3.1.3. The session bean local interface: MessageManager.java

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.

1.3.1.4. The view: messages.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>
           <h:commandLink value="#{msg.title}" action="#{messageManager.select}"/>
        </h:column>
        <h:column>
           <f:facet name="header">
              <h:outputText value="Date/Time"/>
           </f:facet>
           <h:outputText value="#{msg.datetime}">
              <f:convertDateTime type="both" dateStyle="medium" timeStyle="short"/>
           </h:outputText>
        </h:column>
        <h:column>
           <h:commandButton value="Delete" action="#{messageManager.delete}"/>
        </h:column>
     </h:dataTable>
     <h3><h:outputText value="#{message.title}"/></h3>
     <div><h:outputText value="#{message.text}"/></div>
   </h:form>
  </f:view>
 </body>
</html>

1.3.2. How it works

The first time we navigate to the messages.jsp page, whether by a JSF postback (faces request) or a direct browser GET request (non-faces request), the page will try to resolve the messageList context variable. Since this context variable is not initialized, Seam will call the factory method findMessages(), which performs a query against the database and results in a DataModel being outjected. This DataModel provides the row data needed for rendering the <h:dataTable>.

When the user clicks the <h:commandLink>, JSF calls the select() action listener. Seam intercepts this call and injects the selected row data into the message attribute of the messageManager component. The action listener fires, marking the selected Message as read. At the end of the call, Seam outjects the selected Message to the context variable named message. Next, the EJB container commits the transaction, and the change to the Message is flushed to the database. Finally, the page is re-rendered, redisplaying the message list, and displaying the selected message below it.

If the user clicks the <h:commandButton>, JSF calls the delete() action listener. Seam intercepts this call and injects the selected row data into the message attribute of the messageList component. The action listener fires, removing the selected Message from the list, and also calling remove() on the EntityManager. At the end of the call, Seam refreshes the messageList context variable and clears the context variable named message. The EJB container commits the transaction, and deletes the Message from the database. Finally, the page is re-rendered, redisplaying the message list.

1.4. Seam and jBPM: the todo list example

jBPM provides sophisticated functionality for workflow and task management. To get a small taste of how jBPM integrates with Seam, we'll show you a simple "todo list" application. Since managing lists of tasks is such core functionality for jBPM, there is hardly any Java code at all in this example.

1.4.1. Understanding the code

The center of this example is the jBPM process definition. There are also two JSPs and two trivial JavaBeans (There was no reason to use session beans, since they do not access the database, or have any other transactional behavior). Let's start with the process definition:

Example 1.13. 

<process-definition name="todo">
   
   <start-state name="start">                                                            (1)
      <transition to="todo"/>
   </start-state>
   
   <task-node name="todo">                                                               (2)
      <task name="todo" description="#{todoList.description}">                           (3)
         <assignment actor-id="#{actor.id}"/>                                            (4)
      </task>
      <transition to="done"/>
   </task-node>
   
   <end-state name="done"/>                                                              (5)
   
</process-definition>
(1)

The <start-state> node represents the logical start of the process. When the process starts, it immediately transitions to the todo node.

(2)

The <task-node> node represents a wait state, where business process execution pauses, waiting for one or more tasks to be performed.

(3)

The <task> element defines a task to be performed by a user. Since there is only one task defined on this node, when it is complete, execution resumes, and we transition to the end state. The task gets its description from a Seam component named todoList (one of the JavaBeans).

(4)

Tasks need to be assigned to a user or group of users when they are created. In this case, the task is assigned to the current user, which we get from a built-in Seam component named actor. Any Seam component may be used to perform task assignment.

(5)

The <end-state> node defines the logical end of the business process. When execution reaches this node, the process instance is destroyed.

If we view this process definition using the process definition editor provided by JBossIDE, this is what it looks like:

This document defines our business process as a graph of nodes. This is the most trivial possible business process: there is one task to be performed, and when that task is complete, the business process ends.

The first JavaBean handles the login screen login.jsp. Its job is just to initialize the jBPM actor id using the actor component. (In a real application, it would also need to authenticate the user.)

Example 1.14. 

@Name("login")
public class Login {
   
   @In(create=true) 
   private Actor actor;
   
   private String user;

   public String getUser() {
      return user;
   }

   public void setUser(String user) {
      this.user = user;
   }
   
   public String login()
   {
      actor.setId(user);
      return "/todo.jsp";
   }
}

Here we see the use of @In(create=true), which tells Seam to create an instance of a component, in this case the component named actor, if none currently exists in the context.

The JSP itself is trivial:

Example 1.15. 

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<f:view>
    <h:form>
      <div>
        <h:inputText value="#{login.user}"/>
        <h:commandButton value="Login" action="#{login.login}"/>
      </div>
    </h:form>
</f:view>
</body>
</html>

The second JavaBean is responsible for starting business process instances, and ending tasks.

Example 1.16. 

@Name("todoList")
public class TodoList {
   
   private String description;
   
   public String getDescription()                                                        (1)
   {
      return description;
   }

   public void setDescription(String description) {
      this.description = description;
   }
   
   @CreateProcess(definition="todo")                                                     (2)
   public void createTodo() {}
   
   @StartTask @EndTask                                                                   (3)
   public void done() {}

}
(1)

The description property accepts user input form the JSP page, and exposes it to the process definition, allowing the task description to be set.

(2)

The Seam @CreateProcess annotation creates a new jBPM process instance for the named process definition.

(3)

The Seam @StartTask annotation starts work on a task. The @EndTask ends the task, and allows the business process execution to resume.

In a more realistic example, @StartTask and @EndTask would not appear on the same method, because there is usually work to be done using the application in order to complete the task.

Finally, the meat of the application is in todo.jsp:

Example 1.17. 

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://jboss.com/products/seam/taglib" prefix="s" %>
<html>
<head>
<title>Todo List</title>
</head>
<body>
<h1>Todo List</h1>
<f:view>
   <h:form id="list">
      <div>
         <h:outputText value="There are no todo items." rendered="#{empty taskInstanceList}"/>
         <h:dataTable value="#{taskInstanceList}" var="task" rendered="#{not empty taskInstanceList}">
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Description"/>
                </f:facet>
                <h:inputText value="#{task.description}"/>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Created"/>
                </f:facet>
                <h:outputText value="#{task.taskMgmtInstance.processInstance.start}">
                    <f:convertDateTime type="date"/>
                </h:outputText>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Priority"/>
                </f:facet>
                <h:inputText value="#{task.priority}" style="width: 30"/>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Due Date"/>
                </f:facet>
                <h:inputText value="#{task.dueDate}" style="width: 100">
                    <f:convertDateTime type="date" dateStyle="short"/>
                </h:inputText>
            </h:column>
            <h:column>
                <s:link value="Done" action="#{todoList.done}" taskInstance="#{task}" linkStyle="button"/>
            </h:column>
         </h:dataTable>
      </div>
      <div>
      <h:messages/>
      </div>
      <div>
         <h:commandButton value="Update Items" action="update"/>
      </div>
   </h:form>
   <h:form id="new">
      <div>
         <h:inputText value="#{todoList.description}"/>
         <h:commandButton value="Create New Item" action="#{todoList.createTodo}"/>
      </div>
   </h:form>
</f:view>
</body>
</html>

Let's take this one piece at a time.

The page renders a list of tasks, which it gets from a built-in Seam component named taskInstanceList. The list is defined inside a JSF form.

<h:form id="list">
   <div>
      <h:outputText value="There are no todo items." rendered="#{empty taskInstanceList}"/>
      <h:dataTable value="#{taskInstanceList}" var="task" rendered="#{not empty taskInstanceList}">
         ...
      </h:dataTable>
   </div>
</h:form>

Each element of the list is an instance of the jBPM class TaskInstance. The following code simply displays the interesting properties of each task in the list. For the description, priority and due date, we use input controls, to allow the user to update these values.

<h:column>
    <f:facet name="header">
       <h:outputText value="Description"/>
    </f:facet>
    <h:inputText value="#{task.description}"/>
</h:column>
<h:column>
    <f:facet name="header">
        <h:outputText value="Created"/>
    </f:facet>
    <h:outputText value="#{task.taskMgmtInstance.processInstance.start}">
        <f:convertDateTime type="date"/>
    </h:outputText>
</h:column>
<h:column>
    <f:facet name="header">
        <h:outputText value="Priority"/>
    </f:facet>
    <h:inputText value="#{task.priority}" style="width: 30"/>
</h:column>
<h:column>
    <f:facet name="header">
        <h:outputText value="Due Date"/>
    </f:facet>
    <h:inputText value="#{task.dueDate}" style="width: 100">
        <f:convertDateTime type="date" dateStyle="short"/>
    </h:inputText>
</h:column>

This button ends the task by calling the action method annotated @StartTask @EndTask. It passes the task id to Seam as a request parameter:

<h:column>
    <s:link value="Done" action="#{todoList.done}" taskInstance="#{task}" linkStyle="button"/>
</h:column>

(Note that this is using a Seam <s:link> JSF control from the seam-ui.jar package.)

This button is used to update the properties of the tasks. When the form is submitted, Seam and jBPM will make any changes to the tasks persistent. There is no need for any action listener method:

<h:commandButton value="Update Items" action="update"/>

A second form on the page is used to create new items, by calling the action method annotated @CreateProcess.

<h:form id="new">
    <div>
        <h:inputText value="#{todoList.description}"/>
        <h:commandButton value="Create New Item" action="#{todoList.createTodo}"/>
    </div>
</h:form>

There are several other files needed for the example, but they are just standard jBPM and Seam configuration and not very interesting.

1.4.2. How it works

TODO

1.5. Seam pageflow: the numberguess example

For Seam applications with relatively freeform (ad hoc) navigation, JSF navigation rules are a perfectly good way to define the page flow. For applications with a more constrained style of navigation, especially for user interfaces which are more stateful, navigation rules make it difficult to really understand the flow of the system. To understand the flow, you need to piece it together from the view pages, the actions and the navigation rules.

Seam allows you to use a jPDL process definition to define pageflow. The simple number guessing example shows how this is done.

1.5.1. Understanding the code

The example is implemented using one JavaBean, three JSP pages and a jPDL pageflow definition. Let's begin with the pageflow:

Example 1.18. 

<pageflow-definition name="numberGuess">
   
   <start-page name="displayGuess" view-id="/numberGuess.jsp">
      <redirect/>
      <transition name="guess" to="evaluateGuess">
          <action expression="#{numberGuess.guess}" />
      </transition>                                                                      (1)
   </start-page>                                                                         (2)
                                                                                         (3)
   <decision name="evaluateGuess" expression="#{numberGuess.correctGuess}">
      <transition name="true" to="win"/>
      <transition name="false" to="evaluateRemainingGuesses"/>
   </decision>                                                                           (4)
   
   <decision name="evaluateRemainingGuesses" expression="#{numberGuess.lastGuess}">
      <transition name="true" to="lose"/>
      <transition name="false" to="displayGuess"/>
   </decision>
   
   <page name="win" view-id="/win.jsp">
      <redirect/>
      <end-conversation />
   </page>
   
   <page name="lose" view-id="/lose.jsp">
      <redirect/>
      <end-conversation />
   </page>
   
</pageflow-definition>
(1)

The <page> element defines a wait state where the system displays a particular JSF view and waits for user input. The view-id is the same JSF view id used in plain JSF navigation rules. The redirect attribute tells Seam to use post-then-redirect when navigating to the page. (This results in friendly browser URLs.)

(2)

The <transition> element names a JSF outcome. The transition is triggered when a JSF action results in that outcome. Execution will then proceed to the next node of the pageflow graph, after invocation of any jBPM transition actions.

(3)

A transition <action> is just like a JSF action, except that it occurs when a jBPM transition occurs. The transition action can invoke any Seam component.

(4)

A <decision> node branches the pageflow, and determines the next node to execute by evaluating a JSF EL expression.

Here is what the pageflow looks like in the JBossIDE pageflow editor:

Now that we have seen the pageflow, it is very, very easy to understand the rest of the application!

Here is the main page of the application, numberGuess.jsp:

Example 1.19. 

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<html>
<head>
<title>Guess a number...</title>
</head>
<body>
<h1>Guess a number...</h1>
<f:view>
    <h:form>
        <h:outputText value="Higher!" rendered="#{numberGuess.randomNumber>numberGuess.currentGuess}" />
        <h:outputText value="Lower!" rendered="#{numberGuess.randomNumber<numberGuess.currentGuess}" />
        <br />
        I'm thinking of a number between <h:outputText value="#{numberGuess.smallest}" /> and 
        <h:outputText value="#{numberGuess.biggest}" />. You have 
        <h:outputText value="#{numberGuess.remainingGuesses}" /> guesses.
        <br />
        Your guess: 
        <h:inputText value="#{numberGuess.currentGuess}" id="guess" required="true">
            <f:validateLongRange
                maximum="#{numberGuess.biggest}" 
                minimum="#{numberGuess.smallest}"/>
        </h:inputText>
        <h:commandButton type="submit" value="Guess" action="guess" />
        <br/>
        <h:message for="guess" style="color: red"/>
    </h:form>
</f:view>
</body>
</html>

Notice how the command button names the guess transition instead of calling an action directly.

The win.jsp page is predictable:

Example 1.20. 

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<html>
<head>
<title>You won!</title>
</head>
<body>
<h1>You won!</h1>
<f:view>
    Yes, the answer was <h:outputText value="#{numberGuess.currentGuess}" />.
    It took you <h:outputText value="#{numberGuess.guessCount}" /> guesses.
    Would you like to <a href="numberGuess.seam">play again</a>?
  </f:view>
</body>
</html>

As is lose.jsp (which I can't be bothered copy/pasting). Finally, the JavaBean Seam component:

Example 1.21. 

@Name("numberGuess")
@Scope(ScopeType.CONVERSATION)
public class NumberGuess {
   
   private int randomNumber;
   private Integer currentGuess;
   private int biggest;
   private int smallest;
   private int guessCount;
   private int maxGuesses;
   
   @Create                                                                               (1)
   @Begin(pageflow="numberGuess")                                                        (2)
   public void begin()
   {
      randomNumber = new Random().nextInt(100);
      guessCount = 0;
      biggest = 100;
      smallest = 1;
   }
   
   public void setCurrentGuess(Integer guess)
   {
      this.currentGuess = guess;
   }
   
   public Integer getCurrentGuess()
   {
      return currentGuess;
   }
   
   public void guess()
   {
      if (currentGuess>randomNumber)
      {
         biggest = currentGuess - 1;
      }
      if (currentGuess<randomNumber)
      {
         smallest = currentGuess + 1;
      }
      guessCount ++;
   }
   
   public boolean isCorrectGuess()
   {
      return currentGuess==randomNumber;
   }
   
   public int getBiggest()
   {
      return biggest;
   }
   
   public int getSmallest()
   {
      return smallest;
   }
   
   public int getGuessCount()
   {
      return guessCount;
   }
   
   public boolean isLastGuess()
   {
      return guessCount==maxGuesses;
   }

   public int getRemainingGuesses() {
      return maxGuesses-guessCount;
   }

   public void setMaxGuesses(int maxGuesses) {
      this.maxGuesses = maxGuesses;
   }

   public int getMaxGuesses() {
      return maxGuesses;
   }

   public int getRandomNumber() {
      return randomNumber;
   }
}
(1)

The first time a JSP page asks for a numberGuess component, Seam will create a new one for it, and the @Create method will be invoked, allowing the component to initialize itself.

(2)

The @Begin annotation starts a Seam conversation (much more about that later), and specifies the pageflow definition to use for the conversation's page flow.

As you can see, this Seam component is pure business logic! It doesn't need to know anything at all about the user interaction flow. This makes the component potentially more reuseable.

1.5.2. How it works

TODO

1.6. A complete Seam application: the Hotel Booking example

1.6.1. Introduction

The booking application is a complete hotel room reservation system incorporating the following features:

  • User registration

  • Login

  • Logout

  • Set password

  • Hotel search

  • Hotel selection

  • Room reservation

  • Reservation confirmation

  • Existing reservation list

The booking application uses JSF, EJB 3.0 and Seam, together with Facelets for the view. There is also a port of this application to JSF, Facelets, Seam, JavaBeans and Hibernate3.

One of the things you'll notice if you play with this application for long enough is that it is extremely robust. You can play with back buttons and browser refresh and opening multiple windows and entering nonsensical data as much as you like and you will find it very difficult to make the application crash. You might think that we spent weeks testing and fixing bugs to achive this. Actually, this is not the case. Seam was designed to make it very straightforward to build robust web applications and a lot of robustness that you are probably used to having to code yourself comes naturally and automatically with Seam.

As you browse the sourcecode of the example application, and learn how the application works, observe how the declarative state management and integrated validation has been used to achieve this robustness.

1.6.2. Overview of the booking example

The project structure is identical to the previous one, to install and deploy this application, please refer to Section 1.1, “Try the examples”. Once you've successfully started the application, you can access it by pointing your browser to http://localhost:8080/seam-booking/

Just ten classes (plus six session beans local interfaces and 1 annotation interface) where used to implement this application. Six session bean action listeners contain all the business logic for the listed features.

  • BookingListAction retrieves existing bookings for the currently logged in user.
  • ChangePasswordAction updates the password of the currently logged in user.
  • HotelBookingAction implements the core functionality of the application: hotel room searching, selection, booking and booking confirmation. This functionality is implemented as a conversation, so this is the most interesting class in the application.
  • LoginAction validates the login details and retrieves the logged in user.
  • LogoutAction ends the login session.
  • RegisterAction registers a new system user.

Three entity beans implement the application's persistent domain model.

  • Hotel is an entity bean that represent a hotel
  • Booking is an entity bean that represents an existing booking
  • User is an entity bean to represents a user who can make hotel bookings

Finally, the LoggedIn annotation and the LoggedInInterceptor are used to protect actions that require a logged in user.

1.6.3. Understanding Seam conversations

We encourage you browse the sourcecode at your pleasure. In this tutorial we'll concentrate upon one particular piece of functionality: hotel search, selection, booking and confirmation. From the point of view of the user, everything from selecting a hotel to confirming a booking is one continuous unit of work, a conversation. Searching, however, is not part of the conversation. The user can select multiple hotels from the same search results page, in different browser tabs.

Most web application architectures have no first class construct to represent a conversation. This causes enormous problems managing state associated with the conversation. Usually, Java web applications use a combination of two techniques: first, some state is thrown into the HttpSession; second, persistable state is flushed to the database after every request, and reconstructed from the database at the beginning of each new request.

Since the database is the least scalable tier, this often results in an utterly unacceptable lack of scalability. Added latency is also a problem, due to the extra traffic to and from the database on every request. To reduce this redundant traffic, Java applications often introduce a data (second-level) cache that keeps commonly accessed data between requests. This cache is necessarily inefficient, because invalidation is based upon an LRU policy instead of being based upon when the user has finished working with the data. Furthermore, because the cache is shared between many concurrent transactions, we've introduced a whole raft of problem's associated with keeping the cached state consistent with the database.

Now consider the state held in the HttpSession. By very careful programming, we might be able to control the size of the session data. This is a lot more difficult than it sounds, since web browsers permit ad hoc non-linear navigation. But suppose we suddenly discover a system requirement that says that a user is allowed to have mutiple concurrent conversations, halfway through the development of the system (this has happened to me). Developing mechanisms to isolate session state associated with different concurrent conversations, and incorporating failsafes to ensure that conversation state is destroyed when the user aborts one of the conversations by closing a browser window or tab is not for the faint hearted (I've implemented this stuff twice so far, once for a client application, once for Seam, but I'm famously psychotic).

Now there is a better way.

Seam introduces the conversation context as a first class construct. You can safely keep conversational state in this context, and be assured that it will have a well-defined lifecycle. Even better, you won't need to be continually pushing data back and forth between the application server and the database, since the conversation context is a natural cache of data that the user is currently working with.

Usually, the components we keep in the conversation context are stateful session beans. (We can also keep entity beans and JavaBeans in the conversation context.) There is an ancient canard in the Java community that stateful session beans are a scalability killer. This may have been true in 1998 when WebFoobar 1.0 was released. It is no longer true today. Application servers like JBoss 4.0 have extremely sophisticated mechanisms for stateful session bean state replication. (For example, the JBoss EJB3 container performs fine-grained replication, replicating only those bean attribute values which actually changed.) Note that all the traditional technical arguments for why stateful beans are inefficient apply equally to the HttpSession, so the practice of shifting state from business tier stateful session bean components to the web session to try and improve performance is unbelievably misguided. It is certainly possible to write unscalable applications using stateful session beans, by using stateful beans incorrectly, or by using them for the wrong thing. But that doesn't mean you should never use them. Anyway, Seam guides you toward a safe usage model. Welcome to 2005.

OK, I'll stop ranting now, and get back to the tutorial.

The booking example application shows how stateful components with different scopes can collaborate together to achieve complex behaviors. The main page of the booking application allows the user to search for hotels. The search results are kept in the Seam session scope. When the user navigates to one of these hotels, a conversation begins, and a conversation scoped component calls back to the session scoped component to retrieve the selected hotel.

The search functionality is implemented using a session-scope stateful session bean, similar to the one we saw in the message list example above.

Example 1.22. 

@Stateful                                                                                (1)
@Name("hotelSearch")
@Scope(ScopeType.SESSION)
@LoggedIn                                                                                (2)
public class HotelSearchingAction implements HotelSearching
{
   
   @PersistenceContext
   private EntityManager em;
   
   private String searchString;
   private int pageSize = 10;
   
   @DataModel                                                                            (3)
   private List<Hotel> hotels;
   @DataModelSelection                                                                   (4)
   private Hotel selectedHotel;
   
   public String find()
   {
      String searchPattern = searchString==null ? "%" : '%' + searchString.toLowerCase().replace('*', '%') + '%';
      hotels = em.createQuery("from Hotel where lower(name) like :search or lower(city) like :search or lower(zip) like :search or lower(address) like :search")
            .setParameter("search", searchPattern)
            .setMaxResults(pageSize)
            .getResultList();
      
      return "main";
   }
   
   public Hotel getSelectedHotel()
   {
      return selectedHotel;
   }
      
   public int getPageSize() {
      return pageSize;
   }

   public void setPageSize(int pageSize) {
      this.pageSize = pageSize;
   }

   public String getSearchString()
   {
      return searchString;
   }

   public void setSearchString(String searchString)
   {
      this.searchString = searchString;
   }
   
   @Destroy @Remove                                                                      (5)
   public void destroy() {}

}
(1)

The EJB standard @Stateful annotation identifies this class as a stateful session bean. Stateful session beans are scoped to the conversation context by default.

(2)

The @LoggedIn annotation applies a custom Seam interceptor to the component. This works because @LoggedIn is marked with an @Interceptor meta-annotation.

(3)

The @DataModel annotation exposes a List as a JSF ListDataModel. This makes it easy to implement clickable lists for search screens. In this case, the list of hotels is exposed to the page as a ListDataModel in the conversation variable named hotels.

(4)

The @DataModelSelection annotation defines a field or setter as holding the selected row for the corresponding @DataModel property.

(5)

The EJB standard @Remove annotation specifies that a stateful session bean should be removed and its state destroyed after invocation of the annotated method. In Seam, all stateful session beans should define a method marked @Destroy @Remove. This is the EJB remove method that will be called when Seam destroys the session context. Actually, the @Destroy annotation is of more general usefulness, since it can be used for any kind of cleanup that should happen when any Seam context ends. If you don't h