Using @RequestParameter and @Observer events model in Seam

<Simple DataTable Example | Seam on Tomcat >

One good thing that Seam adds to JSF is the ability to use GET requests to retrieve data. The JSF spec decided to make every request a post, and this makes it difficult to bookmark pages or fetch pages directly from entering a url along with some parameters.

Well see how this problem is solved in Seam using a simple annotation: @RequestParameter. This annotation allows us to pass a request parameter to our Seam component. Its value is injected before any method is called, guaranteeing us that the request paremeter will be available to us to make use of in our code. A look at our previous example shows us that departmens belong to specific faculties. This means we will need to pass the particular faculty whose departments we want to see on our departmentList.xhtml page displaying departments. Here’s the code that does it in our departmentListing Seam component:

@Stateful

@Name(“departmentListing”)

@Scope(ScopeType.SESSION)

public class DepartmentListBean implements DepartmentList {

@PersistenceContext(type=PersistenceContextType.EXTENDED)

EntityManager em;

@DataModel

List<Department> departments;

@DataModelSelection

private Department department;

@In FacesMessages facesMessages;

@RequestParameter

Integer facId;

@Out(required=false,scope=ScopeType.SESSION)

Faculty faculty;

@Factory(“departments”)

@Observer(“univseam.event.DepartmentChanged”)

public void departmentList() {

//select if a faculty id has been passed to us

if(facId != null){

try {

faculty = (Faculty) em.createQuery(“Select f from Faculty f where f.id=:id”).

setParameter(“id”, facId).getSingleResult();

} catch (NoResultException exception) {

facesMessages.add(“Non-existent faculty passed!”);

return;

}

}

//if the faculty is not null then select its departments

if (faculty != null) {

departments = em.createQuery(“Select d from Department d where d.faculty=:faculty”).

setParameter(“faculty”, faculty).getResultList();

return;

}

facesMessages.add(“No departments under this faculty yet”);

}

Our list of departments is as shown below.

old dept list

The “facId” is a request parameter passed from the page displaying the list of faculties. The method departmentList() is annotated with @Factory(“departments”), which forces Seam to load up the list of departments into the “departments” ArrayList. Seam guarantees that the faculty id will be properly converted to an Integer and placed in the facId variable before we initialize our list of departments, enabling us to first select the particular faculty and pass it to the query to load the departments. Outjecting the faculty object to the Session context is necessary to this discuss and we’ll see why soon.

But how was the request parameter passed? Well, the simple <f:param> tag in JSF allows use to do that. Take a look at this portion of the facultyList.xhtml facelet.

<h:column>

<f:facet name=”header”>Action</f:facet>

<s:link value=”Departments” view=”/departmentList.xhtml”>

<f:param name=”facId” value=”#{faculty.id}”/>

</s:link>

</h:column>

With the use of an <s:link> we are able to append the faculty id to our URL under the name facId and seam binds to that name using the RequestParameter.

Not only is RequestParemeter useful in searching, but can also be very helpful in determining if an entity is being edited or a new one being created. Here we are with a list of departments. We want to use the same page to edit as we do for new department creation. RequestParameter to the rescue.

@Stateful

@Name(“departmentManager”)

public class DepartmentManagerBean implements DepartmentManager {

@In

EntityManager entityManager;

@In

FacesMessages facesMessages;

@In

Faculty faculty;

@Out(required=false)

private Department department;

@RequestParameter(“depId”)

Integer depId;

public void createDepartment() {

//if the department already exists, then allow an edit

if(depId != null){

try {

this.department = (Department) entityManager.createQuery(“Select d from Department d where d.id=:id”).setParameter(“id”, depId).getSingleResult();

} catch (NoResultException noResultException) {

facesMessages.add(“Invalid department”);

}

facesMessages.add(“Editing a department”);

return ;

}

//else instantiate a new department entity

this.department = new Department();

this.department.setFaculty(faculty);

facesMessages.add(“Creating a new department”);

return;

}

Passing the department id enables us to find out if a department like this exists already, in which case we load up that department for editing. If not we create a new department instance, passing it the faculty to which it belongs (which we injected from the previously outjected instance).

Look at how this is done in the departmentList.xhtml facelet.


<h:column>

<f:facet name=”header”>Action</f:facet>

<s:link id=”edit” value=”Edit”

view=”/department.xhtml” action=”#{departmentManager.createDepartment}” propagation=”begin”>

<f:param name=”depId” value=”#{department.id}”/>

</s:link>

</h:column>

The page for modifying/creating a new department is below

new department creation

Again this appends the department id as depId to the URL, and Seam makes it available to our departmentManager to use before the createDepartment method is called. Through this the department.xhtml facelet can be used both for creation and editing of department entities.

I’ve also been looking at alternative ways of automatically refreshing a list when a new element has been added to it in the database. Since @Factory is called only if the referred to object is null, we need another way to reload the list of departments and add the new department as well. Seam has an event mechanism based on the Observer pattern. This allows me to raise events, and all observers of that event immediately get notified of it. After populating the fields of a department I call saveDepartment, which saves my entity to the database. I then raise my own pretentious event: “univseam.event.DepartmentChanged”.

@End

public String saveDepartment() {

if(department.getId()!= null){

entityManager.merge(department);

facesMessages.add(“Department updated!”);

}else{

entityManager.persist(department);

facesMessages.add(“New department added!”);

}

Events.instance().raiseEvent(“univseam.event.DepartmentChanged”);

return “success”;

}

Notice that the factory method that initializes the list of departments (displayed previously) is also annotated with @Observer(“univseam.event.DepartmentChanged”). This time the facId will be null, but faculty is still in session scope, so we are able to reload the list of departments as if it’s nobody’s business.

Updated list of depts

Seam events enable applications to be very stateful, updating themselves when changes happen on the fly. Combining it with request parameters even makes it more fun to do.


Added on 8th April 2008:
Here is the updated link to the source for this tutorial

Advertisements