Rate this page del.icio.us  Digg slashdot StumbleUpon

JBoss Seam: The Next Generation Web Framework

by Michael Juntao Yuan

This article is edited and republished from the original source.

JBoss Seam is a next generation web application framework developed by JBoss, a division of Red Hat. It leverages years of hard work and experience gathered from both the open source community and the Java EE community. The goal of Seam is to make web applications easier to develop, easier to test, better performing, and more scalable. Seam provides built-in support for important web application features that are rarely supported in other frameworks, such as isolated workspaces, business process integration, rules integration, AJAX, and others.

Seam is supported out of the box in the JBoss Application Server. Install the server from the latest JEMS GUI installer and select the ejb3 or ejb3+clustering profile to install proper Seam support libraries. You must run your server in a JDK 5.0 JVM. You can also run Seam applications in Apache Tomcat or plain Java SE 5.0 environment. Refer to the Seam documentation or my book for more details.

To understand why Seam is so special and how it is next generation, we’ll present a brief overview of the history of web applications frameworks.

A Brief History of Java Web Frameworks

When enterprise Java first came out out in the late 1990s, it only had a very primitive framework to build web applications — the Java servlet. A servlet processes web requests, and then prints out HTML markups for display in the browser. The problem with pure Java servlets is that the presentation markups are completely tangled up inside Java code. It is very hard for page designers, who are not Java experts, to work with servlets. From the web framework point of view, the Java servlet is roughly on par with Perl CGI scripts.

Then came the JavaServer Pages (JSP), which allow developers to embed dynamic elements inside HTML pages. Those dynamic elements are backed by Java objects on the server. Developers can hide most Java code by using expression languages and custom tag libraries. However, a JSP by itself still does not provide a clear separation between presentation and business logic. When you submit a form to a JSP page, the page must provide the logic to process the request. In the end, most pure JSP applications have large amounts of Java code scriptlets embedded in between the HTML markups. That makes it difficult for JSP to scale to large enterprise applications. The JSP is roughly on par with PHP in the LAMP world.

To provide a clean separation between presentation and business logic, enterprise Java developers widely adopted the Model-View-Controller (MVC) design pattern. The most popular MVC framework in Java is the Apache Struts action framework. It uses a controller servlet to handle all web requests, including all form posts. The servlet provides the business logic, and then depending on the outcome, forwards to the appropriate JSP page to display.

Evolving from action frameworks, component-based web frameworks have all the benefits of the MVC design pattern as well as several important additional advantages over action frameworks. First, components are self-contained and can be readily reused. They free developers from re-implementing the same UI features over and over. And component-based frameworks open up a big marketplace for components. Second, components encapsulate low level details of browser markup and client side scripts (for example, HTML and JavaScript). The high level of abstraction makes it easy to transparently build new UI features such as AJAX and mobile phone browser support into components. Third, UI components and event handlers are very familiar concepts to desktop UI application developers. Last, it is easy to develop visual tools (for example, drag and drop UI designers in IDEs) for components. The standard and most popular component-based web framework in Java is JavaServer Faces (JSF). It is architecturally similar to ASP.Net in the Microsoft world.

JBoss Seam expands the JSF component model to a new level. It not only supports UI components, but also turns business logic objects, persistence objects, and virtually any Plain Old Java Object (POJO), into managed components. In the process, it eliminates most of the XML artifacts and backing bean boilerplate code that are artificially required in standard JSF. Hence, Seam makes JSF applications much easier and much more pleasant to develop. Furthermore, Seam provides finely grained stateful contexts for components. Those contexts drastically simplify the development of stateful web applications. The rest of this article provides a few examples that give a brief introduction to Seam.

Note:
Seam might support other web frameworks besides JSF in the future. But the core component model in Seam will definitely stay.

Component-based Framework

Seam is first and foremost a component framework. It manages and ties together various visual, business, and persistence components. The best way to understand how Seam works is to look at examples. The following code shows a very simple one-page Seam web application. It takes a visitor’s name in a form, saves the name to the database, and then displays all names in a table, as seen in Figure 1.

<h:form>
  Please enter your name:

  <h:inputText value="#{greeter.name}" size="15"/><br/>
  <h:commandButton type="submit" value="Say Hello"
                      action="#{manager.sayHello}"/>
</h:form>

<f:subview id="allGreeters"
           rendered="#{!empty(allGreeters)}">
<f:verbatim>
<p>The following persons have said "hello" to JBoss Seam:</p>
</f:verbatim>

<h:dataTable value="#{allGreeters}" var="person">
  <h:column>
    <h:outputText value="#{person.name}"/>
  </h:column>
</h:dataTable>
</f:subview>

Figure 1: The simple Seam
web application

Besides the normal XHTML markups, there are several UI components on the page. The <h:inputText> component renders an XHTML text input field, and the <h:commandButton> component renders an XHTML form submit button. When the user clicks on the Say Hello button to submit the form, the <h:inputText> component binds the user input in the text field into the name property of the server-side component named greeter (hence #{greeter.name}), and then invokes the sayHello() method on the manager named component (that is, #{manager.sayHello}) to save the data to the database. Then, when the page is rendered, the <h:dataTable> UI component renders an XHTML table that iterates over #{allGreeters}, which is a Java collection that contains all greeter entries in the database (as we will see later).

What are the greeter and manager components? They are both simple Java POJOs with the @Name annotation, which specifies their names to be referenced from the web page. The allGreeters component is a Java collection outjected from the manager component — we will discuss what is outjection shortly. The following code shows the skeleton of the greeter and manager component classes. We will get to their implementation details later.

... ...
@Name("greeter")
public class Greeter implements Serializable {

  ... ...

  private String name;

  // getter and setter methods for the "name" property
  public String getName() { return name; }
  public void setName(String name) { this.name = name; }
}

... ...
@Name("manager")
public class ManagerAction implements Manager {

  ... ...

  public String sayHello () {
    // Save the current greeter to database and
    // retrieve all greeters from database
  }
}

As you can see, Seam abstracts the HTTP request / response cycles into UI events (for example, the click of the Say Hello button) that trigger actions on business components (for example, setting the greeter.name property and invoking the manager.sayHello method).

Inversion of Control

Now we understand that a Seam application consists of a set of components. Each component has a name. When a component is needed, reference it by name. The Seam runtime creates the component, if it does not exist already, and manage its lifecycle. The practice of delegating component instance creation and destruction to the runtime, is also known as “Inversion of Control” (IoC). The benefit of IoC is that it decouples the implementation of components. For instance, we can use an alternative implementation of the ManagerAction class and the web page does not need to change since it only asks for the manager named component from the Seam runtime. IoC also makes complex relationships between components easier to understand since components relationships are now declared, such as annotations, instead of being created at runtime, for example, to use the new keyword.

It is easy to understand how the web page references back-end components. But how do we communicate between two backend components? For instance, when the manager.sayHello method is invoked, it needs to save the current greeter component instance into the database — how does it get the current greeter component instance? The answer is to use a simple dependency injection annotation @In.

The following code shows how the greeter named component is injected into the manager component. Notice that we do not explicitly give a component name for the injected field variable here. Instead, Seam is smart enough to derive the component name from the field variable name. Of course, you can easily specify a component name if the two do not match.

@Name("manager")
public class ManagerAction implements Manager {

  @In
  private Greeter greeter;

  ... ...
}
	

Besides injecting application component instances, Seam can also inject server-managed component instances. For instance, the @PersistenceContext annotation injects the EntityManager object, which is managed by the server and controls access to the backend relational database. On the other hand, as we already discussed with allGreeters, components can also outject objects into the Seam context so that they can be used by other components in the application. All you need is to annotate the field with the @Out annotation. The following code shows the injection of the EntityManager and outjection of the allGreeters collection.

@Name("manager")
public class ManagerAction implements Manager {

  @In @Out
  private Greeter greeter;

  @Out
  private List <Greeter> allGreeters;

  @PersistenceContext
  private EntityManager em;

  public String sayHello () {
    em.persist (greeter);
    greeter = new Greeter ();
    allGreeters =
        em.createQuery("from Greeter g").getResultList();
    return null;
  }

Object-Relational Mapping

The role of the EntityManager is to bridge data objects and the relational database. It saves objects into the database, queries the database, and turns the query results into data objects. The EntityManager allows our application to work exclusively with Java objects, and avoids the need to mess with SQL relationships. This technique is known as Object Relational Mapping (ORM) and it is proven successful in boosting developer productivity.

Of course, the EntityManager does not operate on just any arbitrary Java object. First, you need the @Entity annotation on the data object class to tell the server that this class needs to be mapped to a relational table. You can optionally give the table name via the @Table annotation. The server creates a column for each JavaBean-style property (that is, fields with corresponding getter and setter methods) in the class. The column name is the same as the mapped property name by default. You also need the @Id annotation to specify the primary key column of the table. The @GeneratedValue annotation indicates that the primary key field is automatically generated when EntityManager saves the object to the database. Below is the full source code of the Greeter class.

@Entity
@Name("greeter")
public class Greeter implements Serializable {

  private long id;
  private String name;

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

  public String getName() { return name; }
  public void setName(String name) { this.name = name; }
}

Stateful Components

The components in this simple example just barely scratch the surface of what Seam can do. But they do demonstrate an important concept in Seam: the lifecycle of Seam components are managed. Refer to components by their names and you never worry about when to create them, where to store them, and when to destroy them. That allows Seam to further simplify application development by tying component lifecycles with application lifecycles. That is a truly powerful feature and a key differentiator between Seam and other web frameworks.

In this section, the Seam Hotel Booking demo application illustrates some key concepts in stateful components. The application source code is found in the examples/booking directory of the Seam distribution. There is also a live demo of the application.

Session scope

Let’s start from a stateful scope most web developers are already familiar with: the HTTP session scope. Components in the session scope are stored in the HTTP session object and remain available until the session is destroyed. The most obvious example of a session-scoped component is the component that holds information about the currently logged in user. This component should be available as long as the user session is valid. So, in the Seam Hotel Booking example, the User component is declared in the session scope.

@Entity
@Name("user")
@Scope(SESSION)
public class User implements Serializable {
   private String username;
   private String password;
   private String name;

   ... ...
}

The user information displayed in each page header comes directly from the session scoped User component. For instance, to display the current logged in user’s name, insert #{user.name} in the JSF page. Thanks to the declarative component model, there is no boilerplate code to set objects into the session and then retrieve it before each use in every request.

Conversation scope

In web frameworks before Seam, the application typically stored all user related state information in the HTTP session. But the problem is that most state objects do not need to live as long as the HTTP session, and hence that is a potential source of memory leak. For instance, a web shopping cart only needs to live until the user checks out. After the checkout, the shopping cart object needs to be destroyed and garbage collected. If an application developer fails to destroy the shopping cart, the memory it takes is not reclaimed until the user logs out or the session times out. As many sites have very long session timeout, this type of memory leak could be serious for high volume sites.

In Seam, we can precisely declare the lifecycle of a component through the conversation scope. A Seam conversation is defined as a series of user interactions across several web pages — you can think of it in terms of a web wizard. The hotelBooking component (implemented by the HotelBookingAction class) in the Seam Hotel Booking example is in the conversation scope. It takes you through several pages to complete a booking; refer to Figure 2 for the last page in the conversation).

The user starts the conversation by clicking a button that invokes the hotelBooking.find method, which is marked with the @Begin annotation. At the end of the booking conversation, the user clicks on a button that invokes the @End annotated hotelBooking.confirm method. The non-annotated methods can be invoked in any order between the @Begin and @End methods. The user can even browse to pages that are not related to the booking and then come back. During that entire time, the hotelBooking component, with all its internal states, remains available to the application. Seam destroys the hotelBooking component after the @End method is invoked.

@Stateful
@Name("hotelBooking")
@Scope(CONVERSATION)
public class HotelBookingAction implements HotelBooking {

   @Begin
   public String find() {
      // Find hotels based on a search criteria
   }

   public String nextHotel() {
     // Navigate the hotel list
   }

   public String selectHotel() {
     // Choose a hotel from the list
   }

   public String bookHotel() {
     // Save selected hotel to database
   }

  @End
  public String confirm() throws InventoryException {
    // Confirm the booking ID
  }

   @End
   public String clear() {
     // Clear the hotel search results
   }

   @Destroy @Remove
   public void destroy() {}

}

Figure 2: Inside a Seam conversation (image links to larger image)

But what if the user aborts the conversation without clicking the button that invokes one of the @End methods? In this example, the conversation would timeout after a pre-configured time. The conversation timeout could still be much shorter than the HTTP session timeout.

The @Begin / @End marked conversations are “long running conversations” in Seam. In fact, all Seam components are in the conversation scope by default and most of them do not need the @Begin / @End annotation at all. The default conversation scope only lives through two pages: the page that instantiates and populates data to the back-end component, and the page that renders the response. The default conversational component can take data from a request, perform required actions, and then render results. Most Seam components work just fine in the default conversation scope.

Note:
Stateful components are crucial for lazy loading in ORM frameworks. Without a stateful context, the EntityManager is valid only inside a single method (that is, the UI event handler method). If it retrieves an object that requires lazy loading of associated entity objects during rendering of the response page, the EntityManager response is to throw an error at runtime since it can no longer access the database when lazy loading takes place after the UI event handler exits. This problem plagues most web frameworks prior to Seam. In Seam, however, you can simply declare the EntityManager to be extended, and it stays valid throughout the lifecycle of the component. So, you are free to use lazy loading to improve your ORM performance.

Concurrent workspaces

Another key benefit Seam conversation has over HTTP sessions is that you can have multiple concurrent conversations in a single HTTP session. By default, each conversation is confined in the browser window or tab where the conversation is started.

So, in the Seam Hotel Booking example, you can open multiple browser tabs and book hotels independently in those tabs. In contrast, in most other web frameworks, progress in different tabs interferes with each other and the user might end up booking the wrong hotel if not careful.

Better yet, the browser BACK button works as expected in all those tabs. Even if you abandon a conversation in a tab by loading another web page using HTTP GET, you can still BACK into the conversation and pick up the booking process from where you left off.

The concurrent workspaces are supported by default in Seam conversations. There is no need to write any additional code.

Business process scope

The conversation scope provides finer grained state management than the HTTP session scope. What if we need components that have longer lifecycles? For instance, we might want to build our web applications according to a pre-defined business process. Each task might take a very long time and multiple users to complete. The state information for the component must persist over multiple user sessions or even server reboots. Before Seam, we would have to manually segment the task data and save them into a relational database to be shared across sessions. That can be a tedious process.

Seam supports a business process stateful context. Using several simple annotations, you can link your stateful business components with processes defined in the jBPM business process engine. jBPM then manages the component’s lifecycle and brings the required actors together to complete the tasks. The business process context makes process-driven applications much easier to develop than before.

In this article, there is not space to explain in detail how business processes work in Seam. However, for an example, look at the dvdstore example application in the Seam distribution. Access a live demo of the this application here. Login as a customer and order some DVDs. When you place an order, a business process is started and it waits for manager approval. Next, log in as a manager and approve / ship the order according to a process you selected (you can select from multiple business processes). Finally, log in as the customer and see the order shipped and the business process completed.

Other Important Seam Features

Stateful components are the most important concept in Seam. In addition to stateful components, Seam provides any feature needed by a modern web application developer. This last section of the article showcases two important Seam features: AJAX and testing support.

AJAX in Seam

Seam uses JSF to render web pages. JSF components provide abstraction for low level HTML / JavaScript details from developers. It is possible to add AJAX functionalities into JSF components without requiring the developer to hand code any JavaScript code. That is a huge productivity gain since JavaScript code can become very messy and error prone. The component model in JSF also fosters a component market place where commercial and open source vendors can provide the developer community with ready-to-use JSF components. In fact, many of those components are already AJAX-enabled. It is very easy to add AJAX features into your JSF / Seam applications. I have used the open source ajax4jsf component library in my Seam applications. It is a very clean and useful library that adds AJAX functionalities to any existing JSF component. Please check out my blog entry for more details.

While using AJAX JSF components is quick and easy, some developers may prefer to write JavaScript directly for custom effects. Seam includes a JavaScript remoting library just for that purpose. With Seam remoting, you can use JavaScript to instantiate or obtain a proxy instance of any server side component in any Seam stateful context. Using the proxy object, you can then make asynchronous JavaScript calls to any server side method, and update the page display based on the current server state. The Seam remoting library delivers the full power of Seam to AJAX applications. The chat example application in the Seam distribution illustrates how to use Seam remoting.

Easy Testing

With the wide spread adoption of agile development methodologies, support for easy testing is now a required feature in any modern software framework. While most other competing web frameworks support unit tests, Seam supports both unit tests and fully integrated tests from the ground up. Seam has a built-in testing framework based on the open source TestNG framework (the next step in the evolution of the popular JUnit). The framework mocks all Seam container services, including component injection, stateful contexts, and database connections, so that your application components can be tested in the development environment (that is, in the Java SE environment) as if they were inside the actual application server.

To see the testing framework in action, run the ant test target in the Seam Hotel booking example. It invokes test cases in the org/jboss.seam.example.booking.test package, and reports the results.

The Seam features covered in this article are just the tip of the iceberg of what Seam can do. But hopefully, they will get you interested in learning more about Seam and how it can improve your web applications!

More resources

About the author

Michael Yuan is the author of four books and a technical evangelist with JBoss. His upcoming book “JBoss Seam: Simplicity and Power Beyond Java EE 5.0″ is the first book on JBoss’s next generation lightweight application framework. Michael has a PhD from the University of Texas at Austin.

Copyright (C) by 2006 Red Hat Inc. This article is licensed under a Creative Commons Attribution 2.5 License (CC BY-SA): http://creativecommons.org/licenses/by-sa/2.5/.

Leave a reply