What is Seam?
JBoss Seam is a "lightweight framework for Java EE 5.0". What does that mean? Isn't Java EE (Enterprise Edition) 5.0 itself a collection of "frameworks"? Why do you need another one that is outside of the official specification? Well, we view Seam as the "missing framework" that should have been included in Java EE 5.0. It sits on top of Java EE 5.0 frameworks to provide a consistent and easy-to-understand programming model for all components in an enterprise web application. It also makes stateful applications and business process-driven applications a breeze to develop. In another words, Seam is all about developer productivity and application scalability.1. Integrate and Enhance Java EE Frameworks
The core frameworks in Java EE 5.0 are EJB (Enterprise JavaBeans) 3.0 and JSF (JavaServer Faces) 1.2. EJB 3.0 (EJB3, hereafter) is a POJO (Plain Old Java Objects) based lightweight framework for business services and database persistence. JSF is a MVC (Model-View-Controller) component framework for web applications. Most Java EE 5.0 web applications will have both EJB3 modules for business logic and JSF modules for the web front end. However, while EJB3 and JSF are complementary to each other, they are designed as separate frameworks each with its own philosophy. For instance, EJB3 uses annotations to configure services, while JSF makes use of XML files. Furthermore, EJB3 and JSF components are not aware of each other at the framework level. To make EJB3 and JSF work together, you need artificial facade objects (i.e., JSF backing beans) to tie business components to web pages, and boilerplate code (a.k.a plumbing code) to make method calls across framework boundaries. Gluing those technologies together is part of Seam's responsibilities.Seam collapses the artificial layer between EJB3 and JSF. It provides a consistent, annotation-based approach to integrate EJB3 and JSF. With a few simple annotations, the EJB3 business components in Seam can now be used directly to back JSF web forms or handle web UI events. Seam allows developers to use the "same kind of stuff", annotated POJOs, for all application components. Compared with applications developed in other web frameworks, Seam applications are conceptually simple and require significantly less code (both in Java and XML) for the same functionalities. If you are impatient and want a quick preview of how simple a Seam application is, you can have a look at the hello world example described lower in this article.
Seam also makes it easy to accomplish tasks that were "difficult" on JSF. For instance, one of the major complaints of JSF is that it relies too much on HTTP POST. It is hard to bookmark a JSF web page and then get it via HTTP GET. Well, with Seam, generating a bookmarkable RESTful web page is very easy. Seam provides a number JSF component tags and annotations that would increase the "web friendliness" and web page efficiency of JSF applications.
At the same time, Seam expands the EJB3 component model to POJOs and brings the stateful context from the web tier to the business components. Furthermore, Seam integrates a number of leading other open source frameworks such as jBPM, JBoss Rules (a.k.a Drools), JBoss Portal, JBoss Microcontainer etc. Seam not only "wire them together" but also enhance the frameworks in similar ways it does to the JSF + EJB3 combination.
While Seam is rooted in Java EE 5.0, its application is not limited to Java EE 5.0 servers. Your Seam applications can be deployed in J2EE 1.4 application servers as well as in plain Tomcat servers. That means you can obtain production support for your Seam applications today!
1 + 1 > 2
It would be a mistake to think that Seam is just another integration framework that wires various frameworks together. Seam provides its own managed stateful context that allows the frameworks to deeply integrate with others via annotations, EL (Expression Language) expressions etc. That level of integration comes from Seam developer's intimate knowledge of the third party frameworks.
2. A Web Frameworks that Understands ORM
Object Relational Mapping (ORM) solutions are widely used in today's enterprise applications. However, most current business and web frameworks are not designed for ORM. They do not manage the persistence context over the entire web interaction lifecycle from the request comes in to the response is fully rendered. That has resulted in all kinds of ORM errors included the dreaded LazyInitializationException, and gave rise to ugly hacks like the "Data Transfer Object" (DTO).
Seam was invented by Gavin King, the inventor of the most popular ORM solution in the world (Hibernate). It is designed from the ground up to promote ORM best practices. With Seam, there is no more DTOs to write; lazy loading just works; and the ORM performance can be greatly improved since the extended persistence context acts as a natural cache to reduce database round trips.
Furthermore, since Seam integrates the ORM layer with the business and presentation layer, we can display ORM objects direct, you can even use database validator annotations on input forms , and redirect ORM exceptions to custom error pages.
3. Designed for Stateful Web Applications
Seam is designed for stateful web applications. Web applications are inherently multi-user applications, and e-commerce applications are inherently stateful and transactional. However, most existing web application frameworks are geared toward stateless applications. You have to fiddle with the HTTP session objects to manage user states. That not only clutters your application with code un-related to the core business logic, but also brings on an array of performance issues.
In Seam, all the basic application components are inherently stateful. They are much easier to use than the HTTP session since their states are declaratively managed by Seam. There is no need to write distracting state management code in a Seam application -- just annotate the component with its scope, lifecycle methods, and other stateful properties -- and Seam takes over the rest. Seam stateful components also provide much finer control over user states than the plain HTTP session does. For instance, you can have multiple "conversations", each consisting of a sequence of web requests and business method calls, in a HTTP session.
Furthermore, database caches and transactions can be automatically tied with the application state in Seam. Seam automatically holds database updates in memory and only commits to the database at the end of a conversation. The in-memory cache greatly reduces database load in complex stateful applications.
In addition to all the above, Seam takes state management in web applications a big step further by supporting integration with the Open Source JBoss jBPM business process engine. You can now specify the work flows of different people in the organization (i.e., customers, managers, technical support etc.) and use the work flow to drive the application, instead of relying on the UI event handlers and databases.
4. Web 2.0 Ready
Seam is fully optimized for Web 2.0 style applications. It provides multiple ways for AJAX (Asynchronous JavaScript And XML, a technology to add interactivity to web pages) support -- from drop-in JavaScript-less AJAX components, AJAX-enabling existing JSF components, to a custom JavaScript library that provide direct access to Seam server components from the browser as Javascript objects. Internally, Seam provides an advanced concurrency model to efficiently manage multiple AJAX requests from the same user.
A big challenge for AJAX applications is the increased database load. An AJAX application makes much more frequent requests to the server than its non-AJAX counterpart does. If all those AJAX requests have to be served by the database, the database would not be able to handle the load. The stateful persistence context in Seam acts as an in-memory cache. It can hold information throughout a long running conversation, and hence helps to reduce the database round trips.
Web 2.0 applications also tend to employ complex relational models for its data (e.g., a social network site is all about managing and presenting the relationships between "users"). For those sites, lazy loading in the ORM layer is crucial. Otherwise, a single query could cascade to loading the entire database. As we discussed earlier, Seam is the only web framework today that supports lazy loading correctly for web applications.
5. POJO Services via Dependency Bijection
Seam is a "lightweight framework" because it promotes the use of POJO (plain old Java objects) as service components. There are no framework interfaces or abstract classes to "hook" components into the application. The question, of course, is how do those POJOs interact with each other to form an application? How do they interact with container services (e.g., the database persistence service)?
Seam wires POJO components together using a popular design pattern known as "dependency injection" (DI). Under this pattern, the Seam framework manages the lifecyle of all the components. When a component needs to use another, it declares this dependency to Seam using annotations. Seam determines where to get this dependent component based on the application's current state and "injects" it into the asking component.
Expanding on the dependency injection concept, a Seam component A can also create another component B and "outjects" the created component B back to Seam for other components, such as C, to use later.
This type of bi-directional dependency management is widely used in even the simplest Seam web applications (e.g., the hello world example in Chapter 2). In Seam terms, we call this "dependency bijection".
6. Configuration by Exception
The key design principal that makes Seam so easy to use is "Configuration by exception". The idea is to have a set of common-sense default behavior for the components. The developer only needs to configure the component explicitly when the desired behavior is not the default. For instance, when Seam injects component A as a property of component B, the Seam name of component A defaults to the recipient property name in component B. There are many little things like that in Seam. The overall result is that configuration metadata in Seam is much simpler than competing Java frameworks. As a result, most Seam applications can be adequately configured with a small number of simple Java annotations. Developers benefit from reduced complexity and, in the end, much less lines of code for the same functionalities developed in competing frameworks.
7. Avoid XML Abuse
As you probably noticed, Java annotations play a crucial role in expressing and managing Seam configuration metadata. That is done by design to make the framework easier to work with.
In the early days of J2EE, XML was viewed as the "holy grail" for configuration management. Framework designers throw all kinds of configuration information, including Java class and method names, in XML files without much thought about the consequence to developers. In retrospect, that was a big mistake. XML configuration files are highly repetitive. They have to repeat information already in the code in order to connect the configuration to the code. Those repetitions make the application prone to minor errors (e.g., a mis-spelled class name would show up as an hard-to-debug error at runtime). The lack of reasonable default configuration settings further compounds this problem. In fact, in some frameworks, the amount of boilerplate code disguised as XML may rival or even exceed the amount of actual Java code in the application. For J2EE developers, this abuse of XML is commonly known as the "XML hell".
The enterprise Java community recognizes this problem with XML abuse and has very successful attempts to replace XML files with annotations in Java source code. EJB3 is the effort by the official Java standardization body to promote the use of annotations in enterprise Java components. EJB3 makes XML files completely optional, and it is definitely a step toward the right direction. Seam adds to EJB3 annotations and expands the annotation-based programming model to the entire web application.
Of course, XML is not entirely bad for configuration data. Seam designers recognize that XML is well-suited to specify web application pages flows or define business process work flows. The XML file allows us to centrally manage the work flow for the entire application, as opposed to scatter the information around in Java source files. The work flow information has little coupling with the source code -- and hence the XML files do not need to duplicate typed information already available in the code.
8. Designed for Testing
Seam is designed from ground up for easy testing. Since all Seam components are just annotated POJOs, they are very easy to unit test. You can just create instances of the POJOs using the regular Java new keyword and then run any methods in your testing framework (e.g., JUnit or TestNG). If you need to test the interaction between multiple Seam components, you can instantiate those components individually and then setup their relationships manually (i.e., use the setter methods explicitly instead of relying on Seam's dependency injection features).
For integrated testing of the entire Seam application, it is a little more complex since you have to run the application inside a Seam container. Seam comes with an embeddable lightweight container that helps this type of testing. In your test framework, you can load the Seam container programatically and then run the tests.
9. Great Tools Support
Tools support is crucial for an application framework that focuses on developer productivity. Seam is distributed with a command line application generator called Seam Gen. Seam Gen closely resembles the tools available in Ruby-On-Rails. It supports features like generating complete CRUD applications from a database, quick developer turn around for web applications via the "edit / save / reload browser" actions, testing support etc.
But more importantly, Seam Gen generated projects work out-of-the-box with leading Java IDEs such as Eclipse and NetBeans. With Seam Gen, you can get started with Seam in no time!
10. Let's Start Coding!
In a nutshell, Seam simplifies the developer overhead for Java EE applications, and at the same time, adds powerful new features beyond Java EE 5.0. In this next section (excerpted from chapter 2 in the book), we will show you some real code examples to illustrate how Seam works. You can find the source code download for all example applications in the book from the book web site http://www.michaelyuan.com/seam/.
Seam Hello World
The most basic and widely used functionality of JBoss Seam is to be the glue between EJB3 and JSF. Seam allows seamless (no pun intended!) integration between the two frameworks through managed components. It extends the EJB3 annotated POJO (plain old Java objects) programming model to the entire web application. There is no more artificially required JNDI lookup, verbose JSF backing bean declaration, excessive facade business methods, and painstakingly passing objects between tiers etc.
Continue to use Java EE patterns in Seam
In traditional Java EE applications, some design patterns, such as JNDI lookup, XML declaration of components, value objects, business facade, are mandatory. Seam eliminates those artificial requirements with annotated POJOs. However, you are still free to use those patterns when they are truely needed in your Seam applications.
Writing a Seam web application is conceptually very simple. You just need to code the following components:
- Entity objects represent the data model. The entity objects could be entity beans in the Java Persistence API (JPA, a.k.a, EJB3 persistence) or Hibernate POJOs. They are automatically mapped to relational database tables.
- JSF web pages display the user interface. The pages capture user input via forms and display result data. The form fields and data display tables are mapped to entity beans or collections of entity beans.
- EJB3 session beans or annotated Seam POJOs act as UI event handlers for the JSF web pages. They process user input encapsulated in entity beans and generate data objects for display in the next step (or page).
All the above components are managed by Seam and they are automatically injected into the right pages / objects at runtime. For instance, when the user clicks a button to submit a JSF form, Seam automatically parses the form fields and constructs an entity bean. Then, Seam passes the entity bean into the event handler session bean, which is also created by Seam, for processing. You do not need to manage component lifecycles and relationships between components in your own code. There is no boilerplate code and no XML file for dependency management.
In this chapter, we use a hello world example to show exactly how Seam glues together a web application. The example application works like this: The user can enter her name on a web form to "say hello" to Seam. Once she submits, the application saves her name to a relational database and displays all the users that have said hello to Seam. The example project is in the HelloWorld folder in the source code download for this book. To build it, you must have Apache ANT 1.6+ (http://ant.apache.org/) installed. Enter the helloworld directory and run the command ant. The build result is the build/jars/helloworld.ear file, which you can directly copy into your JBoss AS instance's server/default/deploy directory. Now, start JBoss AS, and the application is available at the URL http://localhost:8080/helloworld/.
Install JBoss AS
To run examples in the book, we recommend you to use the JEMS (JBoss Enterprise Middleware Suite) GUI installer to install a Seam-compatible version of JBoss AS. The JEMS installer can be downloaded from http://labs.jboss.com/portal/jemsinstaller/downloads. Please refer to Appendix A, Install and Deploy JBoss AS if you need further help on JBoss AS installation and application deployment.
You are welcome to use the sample application as a template to jump start your own Seam projects (see Appendix B, Use Example Applications as Templates). Or, you can use the command line tool Seam Gen (see Chapter 4, Rapid Application Development Tools) to automatically generate project templates, including all configuration files, for you. In this chapter, we will not spend too much time explaining the details of the directory structure in the source code project. Instead, we focus on the code and configuration artifacts a developer must write or manage to build a Seam application. This way, you can apply the knowledge to any project structure without being confined to our template.
Source Code Directories
A Seam application consists of Java classes and XML/text configuration files. In the book's example projects, the Java source code files are in the src directory, the web pages are in the view directory, and all configuration files are in the resources directory. See more in Appendix B, Use Example Applications as Templates.
1. Create a Data Model
The data model in the hello world application is simply a Person class with a name and an id property. The @Entity annotation tells the container to map this class to a relational database table, with each property a column in the table. Each Person instance corresponds to a row of data in the table. Since Seam is "configuration by exception", the container simply uses the class name property name for the table name and column name. The @Id and @GeneratedValue annotations on the id property indicates that the id column is for the primary key and its value is automatically generated by the application server for each Person object saved into the database.
@Entity
@Name("person")
public class Person 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;}
}
The most important annotation in the Person class is the @Name annotation. It specifies the string name the Person bean should be registered under Seam. In other Seam components (e.g., pages and session beans), you can reference the managed Person bean for this component using the "person" name.
2. Map the Data Model to a Web Form
In the JSF page, we use the Person bean to back the form input text field. The #{person.name} symbol refers to the name property on the Seam component named "person", which is an instance of the Person entity bean.
<h:form>
Please enter your name:<br/>
<h:inputText value="#{person.name}" size="15"/><br/>
<h:commandButton type="submit" value="Say Hello"
action="#{manager.sayHello}"/>
</h:form>
Below the entry form, the JSF page displays all people who has said "hello" to Seam in the database. The list of people is stored in a Seam component named "fans". The fans component is a List <Person> object. The JSF dataTable iterates through the list and displays each Person object in a row. The fan symbol is the iterator for the fans list. Figure 2.1, The Hello World web page shows the web page.
<h:dataTable value="#{fans}" var="fan">
<h:column>
<h:outputText value="#{fan.name}"/>
</h:column>
</h:dataTable>
Figure 2.1. The Hello World web page
When the user clicks on the "Say Hello" button to submit the form, Seam creates the person managed component with the input data. It then invokes the sayHello() method on Seam component named "manager" (i.e., #{manager.sayHello} is the UI event handler for the form submit button), which saves the person object to the database and refreshes the fans list. The manager component is an EJB3 session bean and we will discuss it in the next section.
3. Handle Web Events
The manager component in Seam is the ManagerAction session bean, as specified by the @Name annotation on the class. The ManagerAction class has person and fans fields annotated with the @In and @Out annotations.
@Stateless
@Name("manager")
public class ManagerAction implements Manager {
@In @Out
private Person person;
@Out
private List <Person> fans;
The @In and @Out annotations are at the heart of the Seam programming model. So, let's look at exactly what they do here.
What is bijection
In Seam documentation, you sometimes see the term "bijection". That refers to the two-way injection and outjection interaction between Seam components and the Seam managed context.
Since the person field already contains the form data via injection, the sayHello() method simply saves it to the database via the JPA EntityManager, which is injected via the @PersistenceContext annotation. Then it refreshes the fans and person objects, which are outjected after the method exits. The sayHello() returns null to indicate that the current JSF page will be re-displayed with the most up-to-date model data after the call.
@PersistenceContext
private EntityManager em;
public String sayHello () {
em.persist (person);
person = new Person ();
fans = em.createQuery("select p from Person p")
.getResultList();
return null;
}
We are almost done, except for one little thing. As you probably noticed, the ManagerAction bean class implements the Manager interface. In order to conform to the EJB3 session bean specification, we need an interface that lists all the business methods in the bean. Below is the code for the Manager interface. Fortunately, it is easy to automatically generate this interface from any modern IDE tool.
@Local
public interface Manager {
public String sayHello ();
}
That is all the code you need for the Hello World example. In the next two sections, we cover alternative ways to do things and the configuration of Seam applications. You can skip the rest of the chapter for now if you want to jump right into the code and customize the helloworld project for your own small database application.
4. Better Understand the Seam Programming Model
Now we have rushed through the Hello World example application. But we have left off some important topics, such as alternative ways to do things and important features not covered by the above code. In this section, let's go through those topics. They help you gain a deeper understanding of Seam. But for the impatient, you can skip this section and come back later.
4.1. Seam POJO Components
In the above example, we used an EJB3 session bean to implement the application logic. But we are not limited to use EJB3 components in Seam. In fact, in Seam, any POJO with a @Name annotation can be turned into a managed component.
For instance, we can make ManagerAction a POJO instead of a EJB3 session bean.
@Name("manager")
public class ManagerAction {
@In (create=true)
private EntityManager em;
... ...
}
Using POJOs to replace EJB3 beans has pros and cons. POJOs are slightly simpler to program since they do not require EJB3-specific annotations and interfaces (see above). If all your business components are Seam POJOs, you can run your Seam application outside of the EJB3 application server (see Chapter 23, Seam Without EJB3).
However, POJOs also have less features than EJB3 components since POJOs cannot get EJB3 container services. Examples of EJB3 services you lose in non-EJB3 Seam POJOs include the following.
- The @PersistenceContext injection no longer works in POJOs. In order to obtain an EntityManager in a Seam POJO, you have to initialize the EntityManager in Seam configuration file and then use the Seam @In annotation to inject it into the POJO.
- There is no support for declarative method-level transaction in POJOs. Instead, you can configure Seam to demarcate a database transaction from when the web request is received until the response page is rendered.
- Seam POJOs cannot be message-driven components.
- No support for @Asynchronous methods.
- No support for container managed security.
- No transaction or component level persistence contexts. All persistence contexts in Seam POJOs are "extended" (see Section 7.1,"The Default Conversation Scope" for more details).
- No integration into the container's management architecture (ie. JMX console services).
- No Java remoting (RMI) into Seam POJO methods.
- Seam POJOs cannot be @WebService components.
- No JCA integration.
So, why would anyone want to use POJO components when deploying in an EJB3 container? Well, POJO components are good for pure "business logic" components, which delegate data access, messaging, and other infrastructure functionalities to other components. For instance, we can use POJO components to manage Seam data access objects. The "business logic" POJO is useful since they can be re-used in other frameworks if you need to. But all in all, their application is much smaller than EJB3 components, especially in small to middle sized applications. So, in most examples throughout this book, we use EJB3 components.
4.2. Ease of Testing
As we mentioned in Chapter 1, What is Seam, Seam is built from ground up to enable easy and out-of-the-container testing. In the helloworld project, we included two test cases for unit testing and integrated JSF testing in the test folder. The Seam testing infrastructure mocks the database, JSF, Seam context, and other application server services in plain Java SE environment. Just run ant test to run those tests.
4.3. Getter / Setter Based Bijection
In the Hello World example, we demonstrated how to biject Seam components against field variables. You can also biject components against getter and setter methods. For instance, the following code would work just fine.
private Person person;
private List <Person> fans;
@In
public void setPerson (Person person) {
this.person = person;
}
@Out
public Person getPerson () {
return person;
}
@Out
public List <Person> getFans () {
return fans;
}
While the above getter / setter methods are trivial, the real value of bijection via getter / setter methods is that you can add custom logic to manipulate the bijection process. For instance, you can validate the injected object or retrieve the outjected object on the fly from the database.
4.4. Avoid Excessive Bijection
Dependency bijection is a very useful design pattern. However, like any other design pattern, there is always a danger of overusing it. Too much dependency bijection can make the code harder to read since the developer must mentally figure out where each component is injected from. Too much bijection could also adds performance overhead since the bijection happens at runtime.
In the Hello World example, there is a simple way to reduce and even eliminate the bijection: just make the data components properties of the business component. This way, in the JSF pages, we only need to reference the business component and there is no bijection needed to tie the business and data components. For instance, we can change the ManagerAction class to the following.
@Stateless
@Name("manager")
public class ManagerAction implements Manager {
private Person person;
public Person getPerson () {return person;}
public void setPerson (Person person) {
this.person = person;
}
private List <Person> fans;
public List<Person> getFans () {return fans;}
... ...
}
Then, on the web page, we reference the properties as follows.
<h:form>
Please enter your name:<br/>
<h:inputText value="#{manager.person.name}"/>
<br/>
<h:commandButton type="submit" value="Say Hello"
action="#{manager.sayHello}"/>
</h:form>
... ...
<h:dataTable value="#{manager.fans}" var="fan">
<h:column>
<h:outputText value="#{fan.name}"/>
</h:column>
</h:dataTable>
The bottom line is that Seam is versatile when it comes to dependency management. It is generally a good practice to encapsulate the data component with its data access business component. This is especially the case for stateful business components.
4.5. Page navigation in JSF
In this example, we have a single page application. After each button click, JSF re-renders the page with updated data model values. Obviously, most web applications would have more than one page. In JSF, an UI event handler method can determine which page to display next by returning the string name of a navigation rule. For instance, you can define the following navigation rule in the navigation.xml file.
<navigation-case>
<from-outcome>anotherPage</from-outcome>
<to-view-id>/anotherPage.jsp</to-view-id>
</navigation-case>
Then, if the sayHello() method returns the string value "anotherPage", JSF would display the anotherPage.jsp page next. This gives us programatic control over which page to display next from inside the UI event handler method.
4.6. Access database via the EntityManager
The JPA (Java Persistence API, a.k.a EJB3 Entity Bean Persistence) EntityManager manages the mapping between relational database tables and entity bean objects. The EntityManager is created by the application server at runtime. You can inject an EntityManager instance using the @PersistenceContext annotation.
The EntityManager.persist() method saves an entity bean object as a row in its mapped relational table. The EntityManager.query() method runs an SQL-like query to retrieve data from the database in the form of a collection of entity bean objects. Please refer to the JPA documentation for more on how to use the EntityManager and the query language. In this book, we only use the simplest queries.
By default, the EntityManager saves data to the embedded HSQL database. If you are running the application in JBoss AS on the local machine, you can open a GUI console for the HSQL database via the following steps: go to page http://localhost:8080/jmx-console/, click on the database=localDB,service=Hypersonic MBean, and then click on the "invoke" button under the startDatabaseManager method. You can execute any SQL commands against the database from the console.
5. Configuration and Packaging
Next, let's move on to configuration files and application packaging next. You can actually generate almost all the configuration files and build script via the Seam Gen command line utility, or you can simply reuse the ones in the sample application source project. So, if you want to learn Seam programming techniques first and worry about configuration / deployment later, that is fine. You can safely skip this section and come back later when you need it.
In this section, we focus on the Seam EJB3 component configuration here. Seam POJO configuration and deployment outside of JBoss AS is of course also possible.
Most Seam configuration files are XML files. But wait! Hadn't we just promised that Seam would get us out of the "XML hell" in J2EE and Spring? How come it has XML files too? Well, as it turns out, there are some good uses for XML files! XML files are very good for deployment time configuration (e.g., the root URL of the web application and the location of the backend database) because it allows us to make deploy-time changes without changing and re-compiling the code; they are good for gluing different sub-systems in the application server together (e.g., to configure how JSF components interacts with Seam EJB3 components); XML files are also good for presentation related content (e.g., the web page and page navigation flow).
What we are against is to replicate information already exist in the Java source code to XML files. As you will soon see, this simple Seam application has several XML configuration files. All of them are very short and none of them concerns information already available in the Java code. In another word, there is no "XML code" in Seam.
Furthermore, most content in those XML files are fairly static. So, you can easily reuse those files for your own Seam applications. Please refer to Appendix B, Use Example Applications as Templates for instructions on how to use the sample application as a template for your own applications.
We will use the next several pages to detail the configuration files and packaging structure of the sample application. If you are impatient and are happy with the application template, you can skip those. Anyway, without further ado, let's look into how the hello world example application is configured and packaged. To build a deployable Seam application for JBoss AS, we have to package all the above Java classes and configuration files in an Enterprise Application aRchive (EAR) file. In this example, the EAR file is helloworld.ear. It contains three JAR files and two XML configuration files.
helloworld.ear
|+ app.war // Contains web pages etc.
|+ app.jar // Contains Seam components
|+ jboss-seam.jar // The Seam library
|+ META-INF
|+ application.xml
|+ jboss-app.xml
Source Code Directories
In the source code project, the resources/WEB-INF directory contains the configuration files that go into app.war/WEB-INF; the resources/META-INF directory contains files that go into app.jar/META-INF and helloworld.ear/META-INF; the resources directory root has files that go into the root directory of app.jar.
The application.xml file lists the JAR files in the EAR and specifies the root URL for this application.
<application>
<display-name>Seam Hello World</display-name>
<module>
<web>
<web-uri>app.war</web-uri>
<context-root>/helloworld</context-root>
</web>
</module>
<module>
<ejb>app.jar</ejb>
</module>
<module>
<java>jboss-seam.jar</java>
</module>
</application>
The jboss-app.xml file specifies the class loader for this application. Each EAR application should have a unique string name for the class loader. Here, we use the application name in the class loader name to avoid repetition.
<jboss-app>
<loader-repository>
helloworld:archive=helloworld.ear
</loader-repository>
</jboss-app>
The jboss-seam.jar file is the Seam library JAR file from the Seam distribution. The app.war and app.jar files are built by us. So, let's look into the app.war and app.jar files next.
5.1. The WAR file
The app.war file is a JAR file packaged to the Web Application aRchive (WAR) specification. It contains the web pages as well as standard JSF / Seam configuration files. You can also put JSF-specific library files in the WEB-INF/lib directory (e.g., the jboss-seam-ui.jar).
app.war
|+ hello.jsp
|+ index.html
|+ WEB-INF
|+ web.xml
|+ faces-config.xml
|+ components.xml
|+ navigation.xml
The web.xml file is required by all Java EE web applications. JSF uses it to configure the JSF controller servlet and Seam uses it intercept all web requests. The configuration in this file is pretty standard.
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="..."
xsi:schemaLocation="...">
<!-- 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>
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/navigation.xml</param-value>
</context-param>
</web-app>
The faces-config.xml file is a standard configuration file for JSF. Seam uses it to add the Seam interceptor into the JSF lifecycle.
<faces-config>
<lifecycle>
<phase-listener>
org.jboss.seam.jsf.SeamPhaseListener
</phase-listener>
</lifecycle>
</faces-config>
The navigation.xml file contains JSF page navigation rules for multi-page applications. Since the hello world example only has a single page, this file is empty here.
The components.xml file contains Seam-specific configuration options. It is also pretty much application-independent with the exception of the jndi-pattern property, which must include the EAR file's base name for Seam to access EJB3 beans by their full JNDI name.
<components ...>
<core:init
jndi-pattern="helloworld/#{ejbName}/local"
debug="false"/>
<core:manager conversation-timeout="120000"/>
</components>
5.2. The Seam Components JAR
The app.jar file contains all EJB3 bean classes (both entity beans and session beans), as well as EJB3 related configuration files.
app.jar
|+ Person.class // entity bean
|+ Manager.class // session bean interface
|+ ManagerAction.class // session bean
|+ seam.properties // empty file but needed
|+ META-INF
|+ ejb-jar.xml
|+ persistence.xml
The seam.properties file is empty here but it is required for JBoss to know that this JAR file contains Seam EJB3 bean classes, and process the annotations accordingly.
The ejb-jar.xml file contains extra configurations that can override or supplement the annotations on EJB3 beans. In a Seam application, it adds the Seam interceptor to all EJB3 classes. We can reuse the same file for all Seam applications.
<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>
The persistence.xml file configures the backend database source for the EJB3 entity beans. In this example, we just use the default HSQL database embedded inside JBoss AS (i.e., the java:/DefaultDS data source).
<persistence>
<persistence-unit name="helloworld">
<provider>
org.hibernate.ejb.HibernatePersistence
</provider>
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.hbm2ddl.auto"
value="create-drop"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
So, that's all the configuration and packaging a simple Seam application needs. We will cover more configuration options and library files as we move to more advanced topics in this book. Again, the simplest way to start your Seam application is not to worry about those configuration files at all and start from a ready-made application template.
6. How is this Simple?
That's it for the hello world application. With three simple Java classes, a JSF page, and a bunch of largely static configuration files, we have a complete database driven web application. The entire application requires less than 30 lines of Java code and no "XML code". However, if you are coming from a PHP background, you might still be asking: "How is this simple? I can do that in PHP with less code!"
Well, the answer is that Seam applications are conceptually much simpler than PHP (or any other scripting language) applications. The Seam component model allows us to add more functionalities to the application in a controlled and maintainable manner. As we will soon see, Seam components make it a breeze to develop stateful and transactional web applications. The object-relational mapping framework (i.e., entity beans) allows us to focus on the abstract data model without having to deal with database-specific SQL statements.
This article was based on an excerpt from chapters 1 and 2 - in the rest of this book, we will discuss how to develop increasingly complex Seam applications using Seam components. See also the table of contents showing all the topics in the book.
See also two interviews with Seam creator Gavin King, from previous news articles about Seam:
- JBoss Seam 1.1 Indepth: An Interview with Gavin King
- JBoss Seam 1.0: rethinking web application architecture
About the author
Dr. Michael Yuan is the author of JBoss Seam: Simplicity and Power Beyond Java EE 5.0 - a book on next generation web application frameworks. He is also the author of Nokia Smartphone Hacks and other 3 technology books. Michael specializes in lightweight enterprise / web application, and end-to-end mobile application development. You can contact him via his blog.