Sample Application using JSF, Seam 2.0,
and Java Persistence APIs on Glassfish V2
This Sample Store Catalog app demonstrates the usage of JavaServer
Faces, a Catalog Stateful Session Bean, the Java Persistence APIs, and
Seam 2. I took this example
Sample
Application using JSF, Catalog Facade
Stateless Session, and Java Persistence APIs and refactored
it to use Seam on Glassfish by following the steps in Brian Leonards
blog
Seam
Refresh and the clickable list example in the
Seam
Tutorial.
Download
the Seam Sample Application Code
Explanation of the usage of JSF, Seam, and Java Persistence
APIs in a sample Store Catalog Application
The image below shows the Catalog Listing page, which allows a user to
page through a list of items
in a store.
DataTable JSF component
The List.jsp page uses a JSF
dataTable
component to display a list of
catalog items.
The dataTable component is useful when you want to show a set of
results in a table. In a JavaServer Faces application, the
UIData
component
(the superclass of dataTable) supports binding to a collection of
data objects. It does the
work of iterating over each record in the data source. The HTML
dataTable
renderer
displays the data as an HTML table.
In the
List.jsp
web page the dataTable is defined as shown below: (Note:
Red colors
are for
Java EE
tags, annotations code,
Blue for
Seam specific
and
Green
for
my code
or
variables)
Code Sample from: List.jsp |
<h:dataTable value='#{items}' var='dataTableItem'
border="1"
cellpadding="2" cellspacing="0">
|
The
value attribute of a
dataTable
tag references the data to be included
in the table. The
var
attribute specifies a
name that is used by the components within the
dataTable
tag as an alias to the data referenced in the
value
attribute of
dataTable. In the
dataTable tag from the
List.jsp
page, the
value attribute points to a list
of catalog
items. The
var
attribute points
to a single item in that list. As the
UIData
component iterates through the list, each reference to
dataTableItem points to the current item in the
list.
The dataTable's
value
is bound to the
items
attribute
of the
CatalogBean
class:
Code Sample from: CatalogBean.java |
@Stateful
@Scope(SESSION)
@Name("catalog")
@Interceptors({org.jboss.seam.ejb.SeamInterceptor.class})
public class CatalogBean
implements Serializable, Catalog {
@DataModel
private List<Item> items=null;
@Factory("items")
public void getItems() {
if ((items==null) ||
(index != firstItem) ){
getNextItems();
}
}
|
The
@DataModel
Seam annotation exposes an attribute of type java.util.List to a JSF
page as an instance of
javax.faces.model.DataModel.
The
<h:dataTable> supports data binding to a
collection of data objects represented by a
DataModel
instance.
The data
collection underlying a DataModel instance is modeled as a collection
of row objects that can be accessed by a row index. The APIs
provide mechanisms to position to a specified row index, and to
retrieve an object that represents the data that corresponds to the
current row index. In this case, the DataModel is made available
in a session
context variable named
items.
When the List.jsp page is diplayed it will try to resolve the
items
context variable. Since this context variable is not initialized, Seam
will call the
@Factory
method
getItems(),
which performs a JPA query (see
getNextItems() code below)
and results in a
DataModel being
outjected. The
@Factory
annotation tells Seam to invoke the
getItems()
method to initialize the
items value.
The
@Name Seam
annotation specifies
catalog as
the application unique component name which Seam will use to resolve
references to the
catalog
context variable. Seam will instantiate the
component
and bind a new instance to the context variable the first time JSF
encounters the variable name
catalog.
The instance will be bound to the
context specified by the
@Scope
Seam annotation. The
CatalogBean
is a
org.jboss.seam.ScopeType.SESSION
scoped component. This means that the JSF components can bind to
the
catalog
managed bean without configuring this in the faces-config.xml.
The
@Stateful EJB 3.0
annotation marks this as a Stateful EJB. A Stateful EJB is used because
the current chunk of items, and
the user's position in the count of items in the db table, is
maintained for the user's session.
The
@Interceptors EJB
3.0 annotation registers the
SeamInterceptor.class as
an EJB interceptor for this session bean component.
The Seam framework uses
EJB
interceptors to perform bijection, context demarcation, validation,
etc, (the interceptor could be defined in the
ejb-jar.xml
instead).
Column JSF component
On the List.jsp page the Item Name, Photo, and Price properties
are displayed with the
column component:
Code Sample from: List.jsp |
<h:dataTable value='#{items}' var='dataTableItem'
border="1"
cellpadding="2" cellspacing="0">
...
<h:column>
<f:facet name="header">
<h:outputText
value="Price"/>
</f:facet>
<h:outputText
value="#{dataTableItem.price}"/>
</h:column>
|
The column tags represent columns of data in the dataTable component.
While
the dataTable component is iterating over the rows of data, it
processes
the column component associated with each
column tag for each row in
the table. As the dataTable
component iterates through the list, each reference to
dataTableItem points to the
current item in the
list.
The dataTable component iterates through the list of items and
displays the names, photos, and prices. Each
time the dataTable iterates through the list of items, it renders one
row in
each column.
The dataTable and column tags use facets to represent rows of the
table that are not repeated or updated. These include
headers, footers,
and captions.
Java Persistence Query API
The
CatalogBean
Session EJB uses the Java Persistence API
Query object
to return a list of
items.
With the
@PersistenceContext annotation
the CatalogBean uses dependency injection to lookup and obtain a
Container Managed
EntityManager .
Since the EntityManager can be container managed for EJB Session
Beans, the application does not
have to manage its lifecycle (i.e. call the
EntityManagerFactory.create() and EntityManager.close() methods).
Code Sample from: CatalogBean.java |
@DataModel
private List<Item> items=null;
@PersistenceContext(unitName="PetCatalogPu")
private EntityManager em;
private int batchSize =
10;
private int index = 0;
private int firstItem = 0;
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void getNextItems()
{
Query q = em.createQuery("select
object(o) from Item as o");
q.setMaxResults(batchSize);
q.setFirstResult(firstItem);
items= q.getResultList();
index = firstItem;
}
|
Since this query is used for Read-Only browsing, the
transaction
attribute in this example is specified as
NOT_SUPPORTED.
Queries using
transaction-scoped entity managers outside of a transaction are
typically more efficient than queries inside a transaction when the
result type is an entity.
The Java Persistence
Query
APIs are used to create and execute queries that can return a
list of results. The JPA Query interface provides
support for pagination via the setFirstResult() and setMaxResults()
methods:
query.setMaxResults(int maxResult)
sets the maximum number of results to retrieve.
query.setFirstResult(int startPosition)
sets the position of the first result to retrieve.
In the code below, we show the
Item
entity class which maps to the ITEM table that stores the
item instances. This is a
typical Java Persistence entity object. There are two requirements for
an entity:
- annotating the class with an @Entity
annotation.
- annotating the primary key identifier with @Id
Because the fields name, description.... are basic mappings from the
object fields to columns of the same name in the database table, they
don't have to be annotated. The O/R relationships with
Address
and
Product
are also annotated. For more information on
defining JPA entities see
Pro
EJB 3: Java Persistence API book.
Code Sample from: Item.java |
@Entity
@Name("item")
@Scope(ScopeType.EVENT)
public class Item
implements java.io.Serializable {
@Id
private String itemID;
private String name;
private String description;
private String imageurl;
private String imagethumburl;
private BigDecimal price;
@OneToOne(cascade={CascadeType.PERSIST})
private Address address;
@ManyToOne
private Product product;
public Item() { }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
...
}
|
The
@Name
seam annotation specifies the (application unique)
component name
item,
which is used in
the
Detail.jsp to display the selected item's
attributes. The
@Scope
Seam annotation binds the
item
instance to the
org.jboss.seam.ScopeType.EVENT
context.
The
CatalogBean
pages through the list of
items
by
maintaining the
firstItem
and
batchSize
attributes and passing these as
parameters to the
query.setFirstResult(int startPosition),
query.setMaxResults(int maxResult)
methods.
The CatalogBean's scope is defined as
org.jboss.seam.ScopeType.SESSION,
which corresponds to the JSF
managed bean session scope.
The
CatalogBean
itemCount
property is used to get and display
the number of Catologue items in the data base:
Code Sample from: List.jsp |
<h:outputText value="Item #{catalog.firstItem
+
1}..#{catalog.lastItem}
of
#{catalog.itemCount}"/>
|
The
CatalogBean
getItemCount()
method uses the JPA
javax.persistence.Query
interface to get the count of
all items in the database item table:
Code Sample from: CatalogBean.java |
private int itemCount = 0;
public int getItemCount()
{
Query q = entityManager.createQuery("select
count(o) from Item as
o");
itemCount =
((Long)q.getSingleResult()).intValue();
return itemCount;
}
|
A JSF
commandLink is used to provide a link to
click on to
display the next page of items. The
commandLink
tag represents an HTML hyperlink and is rendered as an HTML
<a> element. The
commandLink
tag is used to submit an
action
event to the application.
Code Sample from: List.jsp |
<h:commandLink
action="#{catalog.next}"
value="Next
#{catalog.batchSize}"
rendered="#{catalog.lastItem + catalog.batchSize <=
catalog.itemCount}"/>
|
This
commandLink action attribute
references the CatalogBean
next()
method that calculates
the
next page's first row number and returns a logical outcome
String, which causes the List page to display the next page's
list .
This
CatalogBean
next()
method is defined as shown below:
Code Sample from: CatalogBean.java |
public String next() {
if (firstItem + batchSize
< getItemCount()) {
firstItem += batchSize;
getNextItems();
}
return "item_list";
}
|
The JavaServer Faces
NavigationHandler
matches the logical outcome,
item_list
against the
navigation rules
in the application configuration resource
file
faces-config.xml to
determine which page to access next. In this case, the
JavaServer Faces implementation loads the
List.jsp
page after this method returns.
| Code Sample from: faces-config.xml |
<navigation-rule>
<navigation-case>
<from-outcome>item_list</from-outcome>
<to-view-id>/item/List.jsp</to-view-id>
</navigation-case>
</navigation-rule>
|
A JSF
commandLink
is used to provide a link to click on to
display the previous page of items. This
commandLink
action attribute
references the
CatalogBean's
prev()
method that
calculates the
previous page's first row number and returns a logical outcome
String, which causes the List page to display the previous page of
items :
Code Sample from: List.jsp |
<h:commandLink
action="#{catalog.prev}"
value="Previous
#{catalog.batchSize}"
rendered="#{catalog.firstItem >=catalog.batchSize}"/>
|
This
CatalogBean
prev() method
is defined as shown
below:
Code Sample from: CatalogBean.java |
public String prev()
{
firstItem -= batchSize;
if (firstItem < 0) {
firstItem = 0;
}
getNextItems();
return "item_list";
}
|
A JSF commandLink is used to provide a link to click on to
display a page with the item details. This
commandLink
action attribute
references the
CatalogBean select()
method:
Code Sample from: List.jsp |
<h:column>
<f:facet name="header">
<h:outputText
value="Name"/>
</f:facet>
<h:commandLink
action="#{catalog.select}"
value="#{dataTableItem.name}"/>
</h:column>
|
With Seam if you use the
@DataModelSelection
with the
@DataModel
annotation, when the user clicks on the link, Seam will propagate the
selected row from the DataModel into the
annotated attribute:
Code Sample from: CatalogBean.java
|
@DataModelSelection
@Out(required=false)
private Item item;
public String select() {
return "item_detail";
}
|
The
@DataModelSelection
Seam annotation tells Seam to inject the
DataModel
List
element corresponding to the clicked link into the
item attribute.
The
@Out
Seam annotation transfers the value of this attribute to the
item
event context
variable, making it available to a
JSP page after the
action
catalog.select
method execution. So when a row of the
dataTable is selected, the
selected row
is injected to the
item
attribute of the
CatalogBean Stateful bean, and then
outjected to the event context
variable named
item
which is used in the
Detail.jsp page to display
the item details.
The
CatalogBean
select() returns a string,
"item_detail", which
causes the
Detail.jsp page to display
the item details. The JavaServer Faces
NavigationHandler
matches the logical outcome,
item_detail
against the navigation rules in the application configuration resource
file
faces-config.xml to determine which page to
access next. In this case, the
JavaServer Faces implementation loads the
Detail.jsp
page after this method returns.
| Code Sample from: faces-config.xml
|
<navigation-rule>
<navigation-case>
<from-outcome>item_detail</from-outcome>
<to-view-id>/item/Detail.jsp</to-view-id>
</navigation-case>
</navigation-rule>
|
The Detail.jsp uses the
outputText
component to display the
item
properties:
Code Sample from: Detail.jsp |
<h:outputText
value="#{item.name}"
title="Name" />
<h:outputText value="#{item.description}"
title="Description"/>
<h:graphicImage url="#{item.imageurl}"
title="Imageurl" />
<h:outputText value="#{item.price}"
title="Price" />
<h:outputText value="#{item.address.city}"
title="Address" />
<h:outputText value="#{item.contactinfo.email}"
title="Address"/>
|
Conclusion
This concludes the sample application which demonstrates how to use
Seam
with the
JSF dataTable and
DataModel to page through a
list
of
Item Entities which are retrieved
using the
CatalogBean Stateful Session EJB methods
which use the
Java
Persistence APIs.
Configuration of the Application
for Seam 2.0, JSF, JPA, running on Glassfish V2
First I recommend reading Brian Leonard's
blog
Seam
Refresh . I will
summarize
and update
those steps here:
To Open and Test Run the seampagination Project:
- Use the Resolve Reference Problems dialog to map the ejb and web
modules to their
project, which are subdirectories beneath the seampagination
directory.
- After the references are resolved, right-click the seampagination
project and select Open Required Projects.
- Right-click
the seampagination-EJBModule and select Resolve
Reference
Problems:
- browse to the Seam lib directory and select jboss-seam.jar
and
select Open. This should resove the reference to the following jars:
jboss-seam.jar, hibernate.jar,
hibernate-validator.jar,
hibernate-annotations.jar, hibernate-commons-annotations.jar,
javassist.jar, dom4j.jar, commons-logging.jar.
- Right-click the seampagination-WebModule and select Resolve
Reference
Problems:
- Browse to the seampagination-ejb directory which is a
sub-directory below the seampagination directory and select Open
Project
Folder.
- Browse to the jboss-seam-ui.jar
found in Seam lib
directory. This should resolve the reference to the following
jars:
jboss-seam-ui.jar and
jboss-el.jar.
If you want to create your own Java EE
application using Seam 2.0 on Glassfish V2 with Netbeans from scratch
(read the steps in Brian Leonard's
blog
Seam
Refresh but use the SEAM 2.0 jars
listed here here):
- Use
Netbeans to create a new Enterprise Application
- Right-click the Libraries node of the EJBModule project , choose
Add Jar and add these jars:
- Seam \lib\jboss-seam.jar
- Seam \lib\hibernate.jar
- Seam \lib\hibernate-validator.jar
- Seam \lib\hibernate-annotations.jar
- Seam \lib\hibernate-commons-annotations.jar
- Seam \lib\javassist.jar
- Seam \lib\dom4j.jar
- Seam \lib\commons-logging.jar
- Right-click the Libraries node of the WebModule project ,
choose Add Jar and add these jars:
- your ejbModule
- Seam \lib\jboss-seam-ui.jar
- Seam \lib\jboss-el.jar
- create an empty seam.properties file in the
seampagination-EJBModule src\conf Folder.
- add the following phase listener to your faces-config.xml
file under webpages web-inf:
<lifecycle>
<phase-listener>
org.jboss.seam.jsf.SeamPhaseListener
</phase-listener>
</lifecycle>
- add the following context parameter
to your web.xml file
<context-param>
<param-name>
org.jboss.seam.core.init.jndiPattern
</param-name>
<param-value>
java:comp/env/your ear name/#{ejbName}/local
</param-value>
</context-param>
- add the following listener class to your
web.xml file
<listener>
<listener-class>
org.jboss.seam.servlet.SeamListener
</listener-class>
</listener>
- For any session EJB's referenced from the web, add EJB
references to your web.xml,
for example:
<ejb-local-ref>
<ejb-ref-name>your ear name/CatalogBean/local</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local-home/>
<local>your package name.Catalog</local>
<ejb-link>CatalogBean</ejb-link>
</ejb-local-ref>
- For any EJB's referenced from the web add a Seam
interceptor to
the EJB, for example :
@Interceptors({org.jboss.seam.ejb.SeamInterceptor.class})
References: