First JBoss Seam tutorial

Step by step tutorial to setup a JBoss Seam application

Introduction

This tutorials shows how to setup a application using JBoss Seam,
JSF and Facelets. I will show a configuration for Tomcat and the JBoss
Application Server. Furthermore, you can learn how to use Maven to define
the dependencies.

Version

Tutorial version 1.0 by Sebastian Hennebrueder

Library versions

Download

PDF of this tutorial

http://www.laliluna.de/download/first-jboss-seam-en.pdf

Example project

http://www.laliluna.de/download/first-jboss-seam-source.zip

Marketing

Advanced Training and
Education

Range of advanced trainings on Hibernate, EJB3, JBoss Seam, Design
Patterns, and Enterprise Best Practices

The training can be customized to your needs.

Support, consulting and
development

Get the amount of help you need by experienced trainers and
developers. An hour of support can save you a lot of time, Code and
Design Reviews insures that the best practices are being followed!
Reduce solving and testing time. We can implement a part of your
application, create prototypes or proof of concepts.

Setup the project

Set up a web project with your preferred IDE. We will use a WAR
archive for deployment.

Seam configuration files

We need the following configuration files.

components.xml

This file configures Seam and our own components. I prefer to
configure my components with annotations and not in this file but still
we can do it here as well.

Place: WEB-INF folder in a WAR archive, META-INF folder in a JAR.
We have a WAR archive.

Just tell Seam that we don’t want it to manage transactions (at
the moment).


<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
  xmlns:core="http://jboss.com/products/seam/core"
  xmlns:persistence="http://jboss.com/products/seam/persistence"
  xmlns:security="http://jboss.com/products/seam/security"
  xmlns:drools="http://jboss.com/products/seam/drools"
  xmlns:web="http://jboss.com/products/seam/web"
  xmlns:mail="http://jboss.com/products/seam/mail"
  xmlns:transaction="http://jboss.com/products/seam/transaction"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation=
      "http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.0.xsd
       http://jboss.com/products/seam/persistence http://jboss.com/products/seam/persistence-2.0.xsd
       http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.0.xsd
       http://jboss.com/products/seam/web http://jboss.com/products/seam/web-2.0.xsd
       http://jboss.com/products/seam/drools http://jboss.com/products/seam/drools-2.0.xsd
       http://jboss.com/products/seam/mail http://jboss.com/products/seam/mail-2.0.xsd
       http://jboss.com/products/seam/transaction http://jboss.com/products/seam/transaction-2.0.xsd
       http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.0.xsd">

  <core:init transaction-management-enabled="false"/>
  <transaction:no-transaction/>

</components>

seam.properties

Includes settings which can be used in components.xml. Apart from
this, it is a marker for Seam that a archive (WAR or JAR) includes Seam
components. So just create an empty file.

Place: root source folder (same folder as your Java
classes.

pages.xml

Includes navigation rules and is comparable to the JSF
faces-config.xml. In addition we could add security
rules as well.

Place: WEB-INF of a war project

<?xml version="1.0" encoding="UTF-8"?>
<pages xmlns="http://jboss.com/products/seam/pages"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.0.xsd"
   no-conversation-view-id="/home.xhtml"
   login-view-id="/login.xhtml">

 <page view-id="*">
  <navigation>
   <rule if-outcome="home">
    <redirect view-id="/home.xhtml"/>
   </rule>
  </navigation>
 </page>

 <exception>
  <redirect view-id="/error.xhtml">
   <message>Unexpected error, please try again</message>
  </redirect>
 </exception>

</pages>

faces-config.xml

Normal JavaServer Faces configuration file. The default rendering
of Seam is JavaServer Faces, this is why we need this config
file.

We define default and supported languages and the prefix of the
resource bundle.

This example will use facelets for rendering. This is why we have
to define the facelets view handler in this configuration file.

Do not place JSF navigation rules here as with Seam we will use
the pages.xml for navigation.

<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="1.2"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
  <application>
    <message-bundle>messages</message-bundle>
    <locale-config>
 <default-locale>en</default-locale>
 <supported-locale>en</supported-locale>
    </locale-config>
    <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
  </application>
</faces-config>

web.xml

Place: well it is where it always should be in a webapp

Defines Seam and JSF specific settings, filters and
servlets.

<?xml version="1.0" ?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 id="WebApp_ID" version="2.5">

   <!-- Seam -->    
   <listener>
      <listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
   </listener>
   <filter>
        <filter-name>Seam Filter</filter-name>
        <filter-class>org.jboss.seam.web.SeamFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>Seam Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

  <!-- Facelets development mode (disable in production) --> 
   <context-param>
      <param-name>facelets.DEVELOPMENT</param-name>
      <param-value>true</param-value>
   </context-param>

   <!-- JSF -->    
   <context-param>
      <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
      <param-value>client</param-value>
   </context-param>    
   <context-param>
      <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
      <param-value>.xhtml</param-value>
   </context-param>
   <servlet>
      <servlet-name>Faces Servlet</servlet-name>
      <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
   </servlet>    
   <servlet-mapping>
      <servlet-name>Faces Servlet</servlet-name>
      <url-pattern>*.seam</url-pattern>
   </servlet-mapping>                  
</web-app>

Add libraries

We start with a minimal configured JBoss Seam. As a consequence
with this library set you cannot use Seam Email, Seam PDF, …

JBoss 4.2 provides already the jsf-api and jsf-impl libraries.
Don’t deploy them. Tomcat or Jetty will need them of course.

Manually

Copy the following libraries to the WEB-INF lib folder. You can
find them in the lib folder of your JBoss Seam download.

Maven

Alternatively, you may use Maven. I like it just for managing my
libraries. We need a JBoss repository. My IntelliJ project deployed
unintentionally the el-api.jar which was loaded by Maven. This
provokes a problem on Tomcat 6 and JBoss 4.2. Just make sure that it
is not deployed or exclude it.

Here is the pom.xml file we need:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>de.laliluna</groupId>
  <artifactId>seamWeb</artifactId>

  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>seamWeb</name>
  <url>http://maven.apache.org</url>

  <build>
    <finalName>seamWeb</finalName>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-idea-plugin</artifactId>
        <configuration>
          <downloadSources>true</downloadSources>
          <downloadJavadocs>true</downloadJavadocs>
        </configuration>
      </plugin>
    </plugins>

  </build>
  <repositories>
    <repository>
      <id>repository.jboss.org</id>
      <url>http://repository.jboss.org/maven2</url>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </repository>
  </repositories>

  <dependencies>

    <!-- JBoss Seam minimal -->
    <dependency>
      <groupId>org.jboss.seam</groupId>
      <artifactId>jboss-seam</artifactId>
      <version>2.0.1.GA</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.jboss.seam</groupId>
      <artifactId>jboss-seam-ui</artifactId>
      <version>2.0.1.GA</version>
      <scope>compile</scope>
    </dependency>
    <!-- Seam makes use of Hibernate validator and sadly depends on JTA and JPA even if we don't use it -->
    <dependency>
      <groupId>javax.transaction</groupId>
      <artifactId>jta</artifactId>
      <version>1.1</version>
    </dependency>
    <dependency>
      <groupId>javax.persistence</groupId>
      <artifactId>persistence-api</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>3.0.0.ga</version>
    </dependency>

    <!-- JSF and facelets -->
    <dependency>
      <groupId>javax.faces</groupId>
      <artifactId>jsf-api</artifactId>
      <version>1.2_04-p02</version>
    </dependency>
    <dependency>
      <groupId>javax.faces</groupId>
      <artifactId>jsf-impl</artifactId>
      <version>1.2_04-p02</version>
    </dependency>
    <dependency>
      <groupId>com.sun.facelets</groupId>
      <artifactId>jsf-facelets</artifactId>
      <version>1.1.11</version>
    </dependency>


    <!-- I like to have log4j without Hibernate as well -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.14</version>
      <scope>compile</scope>
    </dependency>

  </dependencies>
</project>

Hello world

Create a index.jsp in the root folder. It will redirect us to the
hello page.

<% response.sendRedirect("hello.seam"); %>

Create the facelets page hello.xhtml.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:s="http://jboss.com/products/seam/taglib"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <head>
    <title>Hello world</title>
  </head>
  <body>
  <p>Welcome to your first Seam application</p>

  </body>
</html>

Deploy your application to Tomcat or JBoss and test it. With my
project name, I used the following URL

http://localhost:8080/seamWeb/

Writing a Seam component

The next step is to improve the hello page. Our application will
deal with hedgehogs and the first thing to be done is to print a list of
hedgehogs.

Create a class Hedgehog.

package de.laliluna.seam;

public class Hedgehog {

  private String name;

  public Hedgehog() {
  }

  public String getName() {
    return name;
  }

  @Override
  public String toString() {
    final StringBuilder sb = new StringBuilder();
    sb.append("Hedgehog");
    sb.append("{name='").append(name).append(''');
    sb.append('}');
    return sb.toString();
  }

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

  public Hedgehog(String name) {
    this.name = name;
  }
}

Create a class HedgehogService. It is a Seam
component. The Name</em></span> annotation marks it as component and defines the name. In addition we can define the context where this component is placed in with the <span class="emphasis"><em>Scope.

We will see later that we can reference the component using the
name. Our service class provides a data model for a JSF data table -
hedgehogs.

package de.laliluna.seam;

import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.datamodel.DataModel;
import org.jboss.seam.ScopeType;
import java.util.List;
import java.util.ArrayList;

@Name("hedgehogService")
@Scope(ScopeType.SESSION)
public class HedgehogService {

  @DataModel
  private List<Hedgehog> hedgehogs;

  public HedgehogService() {
    hedgehogs = new ArrayList<Hedgehog>();
    hedgehogs.add(new Hedgehog("Holger"));
    hedgehogs.add(new Hedgehog("Pete"));
    hedgehogs.add(new Hedgehog("Sebastian"));
  }

  public String getHello() {
    return "Your hedgehogService says hello";
  }
}

Finally we will add some messages to the English resource
bundle.

Create a file messages_en.properties in the src
folder.

hello=Welcome to your Seam application
hedgehog.name=Name

Improve the hello.xhtml to print the hedgehogs,
messages from our resource bundle and the getHello
method.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
        PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:s="http://jboss.com/products/seam/taglib"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<head>
  <title>Hello</title>
</head>
<body>
<p>#{messages.hello}</p>

<p>#{hedgehogService.hello}</p>

<h:dataTable value="#{hedgehogs}" var="h">
  <h:column>
    <f:facet name="header">#{messages['hedgehog.name']}</f:facet>
    #{h.name}
  </h:column>
</h:dataTable>
</body>
</html>

That’s it. Test your application again.

Tips for debugging

When your application starts up it will print all components being
created (Seam and your own) and the context they are placed in. Validate
that your new component is displayed as well.

Seam has a debug page showing useful information.

Add the library jboss-seam-debug.jar in the
WEB-INF folder. Alternatively add the following dependency to your
pom.xml:

 <dependency>
      <groupId>org.jboss.seam</groupId>
      <artifactId>jboss-seam-debug</artifactId>
      <version>2.0.1.GA</version>
    </dependency>

In the components.xml activate the debug
mode.

  <core:init transaction-management-enabled="false" debug="true"/>

Open your browser and point to the following URL to see the debug
page.

http://localhost:8080/seamWeb/debug.seam

Add Hibernate

In this step we will leverage our application to load hedgehogs from
the database.

In this example, I will use a PostgreSQL database. To change the
database, just change the hibernate.cfg.xml settings to your database and
replace the library.

Add libraries

In addition we need the following libraries.

If you prefer Maven, here are the additional dependencies.

 <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate</artifactId>
   <version>3.2.5.ga</version>
   <scope>compile</scope>
 </dependency>
 <dependency>
   <groupId>c3p0</groupId>
   <artifactId>c3p0</artifactId>
   <version>0.9.1.2</version>
   <scope>compile</scope>
 </dependency>
 <dependency>
   <groupId>net.sf.ehcache</groupId>
   <artifactId>ehcache</artifactId>
   <version>1.3.0</version>
 </dependency>
 <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-annotations</artifactId>
   <version>3.3.0.ga</version>
   <scope>compile</scope>
 </dependency>
 <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-commons-annotations</artifactId>
   <version>3.3.0.ga</version>
 </dependency>
 <!-- other -->
 <dependency>
   <groupId>postgresql</groupId>
   <artifactId>postgresql</artifactId>
   <version>8.2-507.jdbc3</version>
   <scope>compile</scope>
 </dependency>

Configuration

We need a Hibernate configuration file hibernate.cfg.xml
in the root source folder.

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
 <!--  postgre SQL configuration-->
 <property name="connection.url">
  jdbc:postgresql://localhost:5432/learninghibernate
 </property>
 <property name="connection.username">postgres</property>
 <property name="connection.password">p</property>
 <property name="connection.driver_class">
 org.postgresql.Driver
 </property>
 <property name="dialect">
  org.hibernate.dialect.PostgreSQLDialect
 </property>
 <property name="c3p0.max_size">4</property>
 <property name="c3p0.min_size">1</property>

 <property name="cache.provider_class">
  org.hibernate.cache.EhCacheProvider
 </property>
 <property name="transaction.factory_class">
  org.hibernate.transaction.JDBCTransactionFactory
 </property>
  <property name="transaction.flush_before_completion">true</property>
  <property name="connection.release_mode">after_statement</property>  
  <property name="hbm2ddl.auto">update</property>
 <property name="default_batch_fetch_size">4</property>
 <mapping class="de.laliluna.seam.Hedgehog" />
</session-factory>
</hibernate-configuration>

In the components.xml we have to change the
transaction handling. The factory is just a work around for a naming
problem. We cannot inject a Hibernate session with the name
session into the transaction, but

I wanted to use this component name in my application.

<!--
  <core:init transaction-management-enabled="false" debug="true"/>
  <transaction:no-transaction/>
  -->

  <core:init debug="true"/>
  <persistence:hibernate-session-factory name="hibernateSessionFactory"/>
  <persistence:managed-hibernate-session name="xsession"
                                         auto-create="true"
                                         session-factory="#{hibernateSessionFactory}"/>
  <transaction:hibernate-transaction session="#{xsession}"  />
  <factory name="session"
           scope="STATELESS"
           auto-create="true"
           value="#{xsession}"/>

Code

We must add the loading and saving of hedgehogs to the
HedgehogService class.

First we inject the component session into our class.

Then we define that the Data model has a factory method which is
loading our hedgehogs.

I changed the Scope to conversation so that the hedgehogs are
reloaded when we create a new hedgehog.

@Name("hedgehogService")
@Scope(ScopeType.CONVERSATION)
public class HedgehogService implements Serializable {
  @In
  private Session session;

  @DataModel
  private List<Hedgehog> hedgehogs;

  @Factory("hedgehogs")
  public void loadHedgehogs() {
    hedgehogs = session.createCriteria(Hedgehog.class).list();
  }

  public void createHedgehog() {
    session.save(new Hedgehog("Name " + new Date()));
  }

The Hedgehog class needs the Hibernate
specific annotations.

@Entity
public class Hedgehog implements Serializable{

  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE)
  private Long id;

// getter and setter are omitted

Frontend

Finally we need to improve our hello.xhtml. We add an action link
to create new hedgehogs.

<s:link action="#{hedgehogService.createHedgehog}">Create a hedgehog</s:link>

<s:fragment rendered="#{empty hedgehogs}">
  No hedgehogs
</s:fragment>
<h:dataTable value="#{hedgehogs}" var="h" rendered="#{! empty hedgehogs}">
  <h:column>
    <f:facet name="header">#{messages['hedgehog.name']}</f:facet>
    #{h.name}
  </h:column>
</h:dataTable>

Conclusion

It is quite simple to create a web application with the JBoss Seam
framework. Though the framework is intended to be an integration framework
for JSF and EJB3, we can easily use it with plain Pojo classes on a Tomcat
application server. We even get transaction handling for free.

The advantage of creating the project manually is that we only have
required libraries instead of the full package.