In my last post, I talked about Seam contexts and how to use them from your application. Now, I’ll show you how you can access them from JSF, especially how to use the event context correctly. At the end, I’ll give you some tips you may find useful when developing the web tier of your application with Seam and JSF.
Accessing contexts from your JSF pages
In Seam, you can access anything saved in the contexts from a JSF page using the #{} notation. For example, consider the following line of code:
<h:outputText value="#{employee.name}" />
This will search all the stateful contexts for the “employee” key using the Contexts.lookupInStatefulContexts() method as I showed you in my last post. Remember that the context search priority is: event, page, conversation, session, business process and application. If the key is not found in any of the contexts, it will return null. Otherwise, it will try to call the getName() method on the object.
JSF and the event context
The event context is the narrowest of the stateful contexts; it only lasts during a single http request. It is very useful when working with data iteration because Seam binds the iterated variable into the event context. Consider the following code fragment:
<rich:dataTable value="#{employees}" var="employee"> <rich:column> <h:outputText value="#{employee.name}" /> </rich:column> <rich:column> <h:outputText value="#{employeeSalaryResolver.netSalary}" /> </rich:column> </rich:dataTable>
In line 6 we are calling another component called employeeSalaryResolver to retrieve the net salary of the employee. Seam binds the actual employee object into the event context, so, we can just inject it in the EmployeeSalaryResolver component:
@Name("employeeSalaryResolver") public class EmployeeSalaryResolver { @In private Employee employee; public double getNetSalary() { double netSalary = employee.getBaseSalary(); // do other complex calculations (extra hours, bonus, etc.) return netSalary; } }
The employee attribute is being injected from the iteration variable (the event context) and used to retrieve the net salary of the employee. This component can be then reused in other use cases.
You can also use the event context to pass information between pages. For example, imagine a component that searches for an employee using the social security number and shows the result in another page.
@Name("searchEmployee") public class SearchEmployee { private String ssNumber; @Out(scope=ScopeType.EVENT,required=false) private Employee employee; public String search() { Employee employee = ...; // search for the employee if (employee != null) { return "success"; } return null; } }
When the search method is called (from a page I’m not showing) and an employee is found, the searchEmployee component will outject the Employee object to the event context and render the result in another page. The navigation in pages.xml would be something like this:
<page view-id="searchEmployee.xhtml"> <navigation> <rule if-outcome="success"> <render view-id="searchResult.xhtml"/> </rule> </navigation> </page>
If you notice, we are using render tag instead of redirect tag. This will forward the request to the searchResult.xhtml page instead of redirecting, so the event context will not be lost.
Seam and JBoss EL
Standard EL (the #{} notation you use in your JSF pages) does not allow you to call methods with predefined parameters. However, Seam overcomes this limitation by using JBoss EL, which is an extension of the standard EL. For example, in our employees application, suppose we have a list of employees and we want to add an option on each row to fire employees. So, we create a fireEmployee component with a fire method that receives an Employee as an argument.
@Name("fireEmployee") public class FireEmployee { public void fireEmployee(Employee employee) { // fire employee } }
Now, we just need to add the action to the employees' page.
<rich:dataTable value="#{employees}" var="employee"> <rich:column> <h:outputText value="#{employee.name}" /> </rich:column> <rich:column> <h:outputText value="#{employeeSalaryResolver.netSalary}" /> </rich:column> <rich:column> <s:link value="Fire Employee" action="#{fireEmployee.fire(employee)}" /> </rich:column> </rich:dataTable>
Really cool feature! Look at the <s:link> tag. Seam introduces some JSF tags to ease development. I won’t describe them here, but if you want, you can check them here.
Tips using Seam and JSF
1. Backing beans
In Seam, you can use EJB’s as your backing beans. However, by default, use JavaBeans (POJO’s). The reason is that you usually bind the backing bean to the page scope, but, if you use EJB Session Beans, you will not be able to do this.
@Name("someBean") @Scope(ScopeType.PAGE) @Synchronized public class SomeBean implements Serializable { private static final long serialVersionUID = -2524956479693778917L; // your code ... }
You can also bind your backing beans to the conversation or session scopes when needed. The @Synchronized annotation tells Seam to serialize requests from the backing beans as concurrency may cause some problems occasionally, especially when you are using AJAX calls. A better approach, however, is to use the eventQueue attribute in your richfaces components. See this for more information.
2. Use a naming convention for your components
You have to choose carefully the name you assign to each of your components because as the application starts growing, you may repeat component names and having some conflicts. So, you can use the package as a prefix for the name of the component.
However, if the project is small, you also can use a simpler prefix if you want. For example, you can use the “controller” prefix for your backing beans, “logic” for your business components and “model” for your entities and DAO’s.
3. You can use more than one Backing Bean per page
A lot of people use one backing bean per page. However, I really discourage this at it does not promotes code reusing and increments coupling. For example, if you have something you show in every page, use a separate backing bean and bind it to the session scope instead of retrieving the same information in every page.
Conclusion
Seam allows you to access any component in any context from JSF, this gives you the opportunity to choose how you want to architect your solution with some cool features that help you speed development time.