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

17 thoughts on “Using @RequestParameter and @Observer events model in Seam

  1. Pingback: bliggi.com

  2. Great tutorial Edem. It’s great to see some coverage of the lesser talked about features of Seam such as @Observer. These are such powerful features, but often overlooked.

    One note. You certainly don’t have to call entityManager.merge(department) in the @End method. You started a conversation, so the entity should still be managed. The update will happen when the transaction ends and the persistence context is flushed. If you want to eagerly flush, perhaps to catch any exception thrown, just call entityManager.flush().

    Merging should only be done when a non-managed entity needs to be brought into a different persistence context than what loaded it. Seam conversations all but eliminate the need to do so.

  3. Nice tutorial! I was looking for a better explanation to both @RequestParameter and @Observer.

    On the link to your tutorial, it is before the changes you made. Is there a link to the full source?

  4. @Scott and @John Hedden
    I don’t have the code with me here. I’ll get it up very soon (maybe tomorrow if i remember to copy it from my other pc).

    Fingers crossed and thanks for the interest.

  5. @Dan Allen
    There have been some cases where I’ve experienced exceptions if i don’t merge the entity used in a conversation. I cannot explain why but I’ve adopted the habit of putting it there to make sure the persistence context does not miss any such update I make. Thanks for the comment though.

  6. Pingback: Seam: Simple Data Table & Conversation Example « Edem Morny’s Tech Blog

  7. Great tutorial,

    when clicking on login there is an exception -home.uni is not available …. cant see it in the source code either is source code up to date

    thanks

  8. Sorry about that login link. I just didn’t implement it. I guess i’ll leave that there for someone else to experiment with implementing. Ciao.

  9. Response to post on April 7, 2008 at 4:09 pm
    @Edem Morny
    You might have experience that while using @PersistenceContext (not extended) and nested conversations, it was my case.

  10. @Bernard
    I don’t remember exactly what made me concoct up this way of using the @Observer and @RequestParameter annotations, but i believe it had something to do with what u just mentioned. Good to hear it solved your problem.

  11. Pingback: NetBeans Setup of Seam 2.0 Applications for Tomcat 6.0″ « Edem Morny’s Tech Blog

  12. I am trying to pass parameter to initialize my Seam component on page request:

    (pages.xml)

    (SLB)

    @RequestParameter(“type”)
    private String _type;

    @Create
    public void method() {…}

    When method() is executed the _type is null always.
    Why is that? _type’s value is supposed to be already injected!..

    Regards,
    Andy.

  13. Andrew,
    The component is created “before” the first request is serverd.

    The initialization parameteres are giving in components.xml.

    On page request, the event fired will use the parameter “_type” to do additional initialization. But that is for the request.

    Carlos

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s