To be useful, your application has to be a multitiered application that uses specific components for the presentation, business, and persistence tiers.
If you have already developed one multitiered application you know that lots of time is devoted to writing lots of “glue” code in order to connect your tier.
Starting from the presentation layer, in your forms you’ll reference your JSF Beans, your JSF Beans will in turn reference your Business classes, your Business classes will call your EJB maybe passing some wrapper Java Bean classes, your EJB will finally reach your DAO layer and do datapersistence.
So how can JBoss Seam help me ? I guess you already have asked yourself: why cannot I reference my EJB directly from my JSP page ?
Let me tell you, Seam it’s not only about this, but the same way when you want to conquer a location in a battle you need a bridgehead, so that’s your bridgehead.
Imagine how beatiful would be if in your JSP form you could reference directly your Entity Beans field, you push “Submit” and the data goes straight on the DB.
Imagine next if you need to query the data, simply reference your Session Bean doQuery Method.
Seam achieves this by eliminating the need for “plumbing” code. Essentially, we are allowing Seam to handle the plumbing and to have the business logic interact with the JSF pages themselves. One of the nice things about Seam is that even if you already have code you want to use, you will be able to
keep your existing EJBs and JSF pages, thus being able to maintain a mixed environment if you choose.
How can Seam do it ? to keep it simple, Seam uses Interceptors at all layer levels. By using interceptors you’re allowed to use Seam specific annotations into already-existing classes. This will cut drammatically the time needed to develop “glue” code and leaving more time to spend actually developing the business functionality.
So let’s get how hands dirty, first of all download Seam from the distribution:
http://www.seamframework.org/Download
In this release we will use the stable 2.0.3 CR1 release. Now let’s see first in detail how to configure Seam Listeners.
I ) web.xml – Seam Servlet Listener
In order to initialize the Seam core services and to correctly manage some Seam contexts that are in common with standard web container contexts, you have to install the Seam servlet listener in your web.xml deployment descriptor:
<web-app>
. . .
<listener>
<listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
</listener>
. . .
</web-app>
Ok, that’s one, let’s go on.
II ) faces-config.xml – JSF Phase Listener
At the heart of Seam’s component and context services is the Seam JSF phase listener. It receives events from the JSF runtime during request processing, allowing Seam to keep its various contexts in their proper state, among other things.
The Seam phase listener is installed in your faces-config.xml file:
<faces-config>
. . .
<lifecycle>
<phase-listener>org.jboss.seam.jsf.SeamPhaseListener</phase-listener>
</lifecycle>
. . .
</faces-config>
III ) ejb-jar.xml – EJB Seam Interceptor
The last tier that needs Seam glue is the EJB-tier. Here we’ll use the old ejb-jar.xml. (Thought you got rid of it, isn’t it ?) Here we just define SeamInterceptor for all the EJBs but of course you could choose to use it only for a subset of them.
<ejb-jar>
. . .
<assembly-descriptor>
<interceptors>
<interceptor>
<interceptor-class>
org.jboss.seam.ejb.SeamInterceptor
</interceptor-class>
</interceptor>
</interceptors>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>
org.jboss.seam.ejb.SeamInterceptor
</interceptor-class>
</interceptor-binding>
</assembly-descriptor>
. . .
</ejb-jar>
The Seam interceptor allows EJB components to be used as Seam components, by “plugging” them into the Seam component life cycle when you annotate your EJBs with the Seam @Name annotation.
JNDI pattern : Seam’s components.xml
In order for Seam to work with the EJB container, you need to include an entry for Seam’s core initialization component, and this entry needs to include a setting for the JNDI pattern that Seam should use to find your application’s EJB components in the JNDI services of the application server.
The following example shows how this setting is provided for the JBoss application
server
<?xml version=”1.0″ encoding=”UTF-8″?>
<components xmlns=”http://jboss.com/products/seam/components”
xmlns:core=”http://jboss.com/products/seam/core”
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/components http://jboss.com/products/seam/components-2.0.xsd“>
<core:init jndi-pattern=”@jndiPattern@”/>
</components>
Seam Registration Example reloaded
Here we’ll use on example application provided with Seam distribution but we’ll add a few additions to make it more interesting. The example is located under the “registration” example folder. Let’s start from the Home page, register.xhtml
<f:view>
<h:form>
<s:validateAll>
<h:panelGrid columns=”2″>
Username: <h:inputText value=”#{user.username}” required=”true”/>
Real Name: <h:inputText value=”#{user.name}” required=”true”/>
Password: <h:inputSecret value=”#{user.password}” required=”true”/>
</h:panelGrid>
</s:validateAll>
<h:messages/>
<h:commandButton value=”Register” action=”#{register.registerUser}“/>
</h:form>
</f:view>
Well if you’re practical with JSF you’ll notice nothing strange except..how does JSF resolves the “user” and “register” fields ? You say in a configuration file ? wrong answer!. JBoss Seam uses , as well as EJB 3.0, a lot of annotations. Let’s see our User Entity Bean and you’ll understand better:
- package org.jboss.seam.example.registration;
-
- import static org.jboss.seam.ScopeType.SESSION;
-
- import java.io.Serializable;
-
- import javax.persistence.Entity;
- import javax.persistence.Id;
- import javax.persistence.Table;
-
- import org.hibernate.validator.Length;
- import org.hibernate.validator.NotNull;
- import org.jboss.seam.annotations.Name;
- import org.jboss.seam.annotations.Scope;
-
- @Entity
- @Name(“user”)
- @Scope(SESSION)
- @Table(name=“users”)
- public class User implements Serializable
- {
- private static final long serialVersionUID = 1881413500711441951L;
-
- private String username;
- 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() {}
-
- @NotNull
- public String getName()
- {
- return name;
- }
-
- public void setName(String name)
- {
- this.name = name;
- }
-
- @NotNull @Length(min=5, max=15)
- public String getPassword()
- {
- return password;
- }
-
- public void setPassword(String password)
- {
- this.password = password;
- }
-
- @Id @NotNull @Length(min=5, max=15)
- public String getUsername()
- {
- return username;
- }
-
- public void setUsername(String username)
- {
- this.username = username;
- }
-
- @Override
- public String toString()
- {
- return “User(“ + username + “)”;
- }
- }
package org.jboss.seam.example.registration;
import static org.jboss.seam.ScopeType.SESSION;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.validator.Length;
import org.hibernate.validator.NotNull;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
@Entity
@Name("user")
@Scope(SESSION)
@Table(name="users")
public class User implements Serializable
{
private static final long serialVersionUID = 1881413500711441951L;
private String username;
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() {}
@NotNull
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
@NotNull @Length(min=5, max=15)
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
@Id @NotNull @Length(min=5, max=15)
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
@Override
public String toString()
{
return "User(" + username + ")";
}
}
As we said, Jboss seam needs a component name specified by the @Name annotation. This name must be unique within the Seam application. So JSF when meets the variable “user” delegates Seam to resolve a context variable with a name “user”.
When Seam instantiates the component, it binds the new instance of a User to a context variable in the component’s default context. The default context is specified using the @Scope annotation. In our case the User bean is a session scoped component
Our Entity Bean has a façade which will be accessed by our clients. The façade is a standard Stateless Session Bean, but with special Jboss Seam Annotations.
Picture 1: presentation tier invoke the RegisterAction EJB.
- package org.jboss.seam.example.registration;
-
- import java.util.List;
-
- import javax.ejb.Stateless;
- import javax.persistence.EntityManager;
- import javax.persistence.PersistenceContext;
-
- import org.jboss.seam.annotations.In;
- import org.jboss.seam.annotations.Logger;
- import org.jboss.seam.annotations.Name;
- import org.jboss.seam.faces.FacesMessages;
- import org.jboss.seam.log.Log;
-
- import org.jboss.seam.annotations.Factory;
- import org.jboss.seam.annotations.datamodel.DataModel;
-
- @Stateless
- @Name(“register”)
- public class RegisterAction implements Register
- {
-
- @In
- private User user;
-
- @PersistenceContext
- private EntityManager em;
-
- @Logger
- private static Log log;
-
- @DataModel
- private List items=null;
-
-
- public String registerUser()
- {
-
- em.persist(user);
- log.info(“Registered new user #{user.username}”);
- return “/registered.jspx”;
-
- }
-
- @Factory(“items”)
- public void getItems() {
- List list = em.createQuery(“From User u order by u.name”).getResultList();
- items = list;
- }
-
- }
package org.jboss.seam.example.registration;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.faces.FacesMessages;
import org.jboss.seam.log.Log;
import org.jboss.seam.annotations.Factory;
import org.jboss.seam.annotations.datamodel.DataModel;
@Stateless
@Name("register")
public class RegisterAction implements Register
{
@In
private User user;
@PersistenceContext
private EntityManager em;
@Logger
private static Log log;
@DataModel
private List items=null;
public String registerUser()
{
em.persist(user);
log.info("Registered new user #{user.username}");
return "/registered.jspx";
}
@Factory("items")
public void getItems() {
List list = em.createQuery("From User u order by u.name").getResultList();
items = list;
}
}
Let’s see in detail: well about the “name” annotation we have already discussed,
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).
The Seam @Logger annotation is used to inject the component’s Log instance.
Let’s move on the registerUser: this is the core method of the application which persist the data on the DB. Notice that it has no parameters because the Bean is injected with the @In annotation.
Maybe you have noticed that this method contains a redirection: well you may asking what happened to JSF navigation rules ? well they still exist but Seam has a nice shortcut which is useful for small application : you can simply return the view as the outcome, eliminating the requirement for a navigation rule. For larger applications of course is better to keep a higher level of indirection and so using navigation rules.
Last piece of the puzzle are @DataModel and the @Factory annotation
Data models are pretty handy components when it’s necessary to display database data onto a presentation tier. This allows you a fairly simple way to look up the data, determine the selected row, and use it for computations and display purposes.
As you’ll see the DataModel will be used to populate the JSF DataTable element with the Values provided in the @Factory method getItems()
<h:dataTable var=”u” value=”#{items}” rendered=”#{items.rowCount>0}“>
<h:column>
<f:facet name=”header”>
<h:outputText value=”username”/>
</f:facet>
<h:outputText value=”#{u.username}“/>
</h:column>
<h:column>
<f:facet name=”header”>
<h:outputText value=”password”/>
</f:facet>
<h:outputText value=”#{u.password}“/>
</h:column>
</h:dataTable>
So when JSF meets the variable “items” will delegate to the @Factory method getItems which will simply create a List of User objects.
@Factory(“items”)
public void getItems() {
List list = em.createQuery(“From User u order by u.name”).getResultList();
items = list;
}
Once instantiated the list will be stored in the variable
@DataModel
private List<User> items=null;
when this is clear, retrieving the single fields is just a piece of cake
<h:outputText value=”#{u.username}”/>
<h:outputText value=”#{u.password}”/>
Picture 2: Data is queried using the RegisterAction’s Factory method.
JBoss Seam Project picture:
So this is how our Seam application looks like.
Picture 3: How a typical Seam project is made up.
Before packing, in the jboss-seam-registration.jar you need to add a seam property file: 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.
One thing you might wonder what is the file is how do you connect to your Datasource: well that’s managed by the persistence.xml file which simply points to the Default HSQL Datasource
<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>
If you want to build this application, the easiest way is simply replace the files that you find under the registration example provided with the Seam distribution. Siimply build and deploy your project with ant and you’re done !
Several Java EE APIs (EJB, JSF, etc.), as well as popular open source frameworks like Spring, make use of the concept of dependency injection. Injection involves the automatic, runtime insertion of a property value into a bean. This simple concept can greatly simplify development, especially in a Java EE environment.
The Seam component model also supports a more general version of dependency injection, called bijection. Standard dependency injection involves a one-time initialization of a bean reference within a component, typically done by some kind of container or other runtime service.
Seam introduces the notion of bijection as a generalization of injection.
In contrast to injection, bijection is:
- contextual – bijection is used to assemble stateful components from various different contexts (a component from a “wider” context may even have a reference to a component from a “narrower” context)
- bidirectional – values are injected from context variables into attributes of the component being invoked, and also outjected from the component attributes back out to the context, allowing the component being invoked to manipulate the values of contextual variables simply by setting its own instance variables
- dynamic – since the value of contextual variables changes over time, and since Seam components are stateful, bijection takes place every time a component is invoked
Before seeing Seam bijection, let’s see what is Simple Injection, from an Example
- @Name(“MyBean”)
- public class MyBean {
- @In
- private OtherBean otherBean;
-
- public SomeBean getOtherBean() {
- return otherBean;
- }
- public void setOtherBean(OtherBean b)
- {
- otherBean = b;
- }
- }
@Name("MyBean")
public class MyBean {
@In
private OtherBean otherBean;
public SomeBean getOtherBean() {
return otherBean;
}
public void setOtherBean(OtherBean b)
{
otherBean = b;
}
}
Here, we’re annotating the formBean property on the Seam component MyBean, telling Seam to inject the value of this property whenever MyBean is invoked. In this case, we’re using @In with no arguments to inject an otherBean instance in MyBean class.
By default, Seam will search all of its contexts for a component with the same name as the annotated property “otherBean.” : the first one it finds (in an ordered search of contexts, starting from the event context and ending with application context) is used as the value of the annotated property. If no component of the given name is found anywhere, the injection will not happen, and the property will remain uninitialized.
JBoss Seam Bijection
As mentioned at the start of this section, Seam extends simple injection by introducing the concept of outjection, or the export of a component value from one component back into the scope where the component lives.
Let’s recall our previous Registration example and add some spice to it. The User Entity Bean will stay the same, we won’t change our model. Rather we add a new Stateless Bean, UserAction
- @Stateless
- @Name(“useraction”)
-
- public class UserAction implements UserItf {
- @In @Out private User user;
- @Out private List userList;
-
- @PersistenceContext private EntityManager em;
-
- public String addAndDisplay() {
- em.persist (user);
- user = new User ();
- userList = em.createQuery(“From User u order by u.name”).getResultList();
- return null;
- }
@Stateless
@Name("useraction")
public class UserAction implements UserItf {
@In @Out private User user;
@Out private List userList;
@PersistenceContext private EntityManager em;
public String addAndDisplay() {
em.persist (user);
user = new User ();
userList = em.createQuery("From User u order by u.name").getResultList();
return null;
}
The useraction component in Seam is the UserAction session bean, as specified by the @Name annotation on the class. The UserAction class has user and userList fields annotated with the @In and @Out annotations.
The @In and @Out annotations are at the heart of the Seam programming model. So, let’s look at exactly what they do here.
The @In annotation tells Seam to assign the user component, which is composed from the JSF form data, to the user field (dependency injection) before executing any method in the session bean. You can specify an arbitrary name for the injected component in @In. But if there is no named specified, as it is here, Seam will just inject the component with the same type and same name as the receiving field variable.
The @Out annotations tell Seam to assign values of the userList and user fields to the managed components of the same names after any method execution. We call this action “dependency outjection” in Seam. This way, in the addAndDisplay method, we simply need to update the user and userList field values and they will be automatically available on the web page.
So the addAndDisplay() method simply saves the fields to the database via the JPA EntityManager,then it refreshes the user and userList objects, which are outjected after the method exits. The addAndDisplay() returns null to indicate that the current JSF page will be re-displayed with the most up-to-date model data after the call.
In short, bijection lets you alias a context variable to a component instance variable, by specifying that the value of the instance variable is injected, outjected, or both.
This is the hello.xhtml view
<f:view>
<h:form>Please enter your info:<br/>
Username: <h:inputText value=”#{user.username}” required=”true”/>
Real Name: <h:inputText value=”#{user.name}” required=”true”/>
Password: <h:inputSecret value=”#{user.password}” required=”true”/>
<h:commandButton type=”submit” value=”Add User” action=”#{useraction.addAndDisplay}“/>
</h:form>
<h:dataTable value=”#{userList}” var=”u”>
<h:column>
<h:outputText value=”#{u.username}“/>
<h:outputText value=”#{u.name}“/>
</h:column>
</h:dataTable>
</f:view>
As you can see, the datatable references the userList, which is populated in the addAndDisplayMethod by the following query
userList = em.createQuery(“From User u order by u.name”).getResultList();
Picture 1: Seam bijection depicted
Getter / Setter Based Bijection
In the above example, we demonstrated Seam bijection against field variables. You can also biject components against getter and setter methods. For instance, take a look at the following code:
- private User user;
- private List userList;
-
- @In public void setUser (User user) {
- this.user = user;
- }
- @Out public User getUser () {
- return user;
- }
- @Out public List getUserList () {
- return userList;
- }
private User user;
private List userList;
@In public void setUser (User user) {
this.user = user;
}
@Out public User getUser () {
return user;
}
@Out public List getUserList () {
return userList;
}
You may wonder why not complicating so much the lift with getter/setter bijection ?
the real value of this change 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.