Skip to main content

How to stay away from the JSF API

Posted by cayhorstmann on January 3, 2010 at 9:54 AM PST

A few weeks ago, Ed Burns posted a link to a blog on the JSF expert group mailing list, commenting “A nice one, but it doesn't mention JSF 2”. Ever the curmudgeon, I pointed out that it wasn't so nice that the blog's sample code used the JSF API in beans when it wasn't necessary—as does in fact a lot of sample code, even in the official Sun tutorials. Ed's response: “Cay, a blog comment by such an eminent citizen as yourself would certainly be noticed.” So, here is the curmudgeonly eminence's advice on how to stay away from the JSF API.

To set the stage, recall the basic mechanism by which JSF links the visual presentation with the application logic. JSF pages are composed of component tags that contain expressions in the oh-so-blandly named Expression Language (EL). For example,

<h:inputSecret value="{userBean.password}"/>

The class of the userBean object must have getters and setters for the password property:

@Named public class UserBean {
   public String getPassword() { ... }
   public void setPassword(String newValue) { ... }
   ...
}

Here, the property has the type String, and that is good. There is no coupling between the UserBean class and the JSF API. You can compile and run unit tests of UserBean without having JSF around. You can even (gasp) switch to another view technology.

So, where do programmers go wrong? Mainly in these four areas:

  1. SelectItem
  2. Data tables
  3. UIComponent
  4. The @ManagedBean annotation

SelectItem

When you show a list of choices, by using radio buttons, checkboxes, listboxes, or dropdown menus, you need to provide a list of (label, value) pairs. Since Java lacks a pair type, the JSF API provides a class SelectItem that lets you specify an item's label, value, and a few other properties that aren't so useful. Your bean provides a read-only property of type Collection<SelectItem> for the JSF component:

<h:selectOneRadio value="#{orderBean.topping}>
   <f:selectItems value="#{orderBean.toppingItems}"/>
</h:selectOneRadio>

@Named public class OrderBean {
   Collection<SelectItem> getToppingItems() { ... }
   ...
}

But to compile this OrderBean, you need the JSF API.

This coupling is easy to avoid. You can use a Map<String, Object> containing the labels and values. (Use a TreeMap if you want the labels in alphabetical order, a LinkedHashMap if you need some other order.)

With JSF 2.0, you can do even better. Expose a Collection<ToppingItem> and use a tag such as the following:

<f:selectItems value="#{orderBean.toppingItems}" 
   var="topping"
   itemLabel="#{topping.name}"
   itemValue="#{topping.id}" />

You design the ToppingItem class in any way you like. The sample above assumes that the class has getName and getId methods.

Data Tables

You can bind a JSF data table to a Collection<RowData>, where RowData is any type of your choice, or you can use the DataModel class in the JSF API. Why do people use a DataModel when it would be so much easier to just use a standard collection? There seems to be one compelling reason—locating the currently selected row in an action.

But you don't need the DataModel class for that. In JSF 1.2, you can use sPAL, which is admittedly a bit of a pain. JSF 2.0 is much better. Simply pass the row as a parameter to your action method.

<h:dataTable value="#{myData.rows}" var="row">
   <h:column>
      <h:commandLink action="#{myData.doSomething(row)}"/>
   </h:column>
  ...
</h:dataTable>

@Named public class MyData {
   public String doSomething(RowData row) {
      do something with row
      return ...;
   }
}

UIComponent

Some design tools (remember Java Studio Creator?) put all JSF components of a form into a bean, sometimes called the form's “backing bean”. This leads to very messy code, and it encourages application logic programmers to call all sorts of inappropriate UIComponent methods. The UIComponent API is difficult for experts to understand, and it really should not be exposed to application programmers.

One reason you may have for poking around in UIComponent objects is multi-component validation. In that case, get busy and write a custom validator so that you can isolate this hokus-pokus from the rest of your application logic. In JSF 2.0, writing a custom validator isn't as bad as it used to be because the tag handling is so much simpler.

@ManagedBean

So, you managed to get all your beans disentangled from the JSF API, you can test them independently, and you are happy. Then you find that JSF 2.0 has another feature that is just too good to pass up: bean annotations that free you from the tedium of faces-config.xml. No more

<managed-bean>
  <managed-bean-name>user</managed-bean-name>
  <managed-bean-class>com.corejsf.UserBean</managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
</managed-bean>

You just use an annotation:

@ManagedBean(name="user")
@RequestScoped
public class UserBean

Except, now you tied your bean once again to the JSF API. The annotations are defined in the javax.faces.bean package.

The solution is not to go back to the tedium of faces-config.xml, but instead to use CDI:

@Named("user")
@RequestScoped
public class UserBean

Ok, so why is this better? First off, @Named is a part of JSR 330, which is tiny and easily integrated in other tools. Of course, @RequestScoped is in javax.enterprise.context, which your test framework would need to support. CDI was built around the assumption that you will wire your beans together in different ways for testing and deployment, and it is just a matter of time before test frameworks buy into that. Or, if you are in a hurry and need to roll your own right now, it makes much more sense to invest in CDI than in JSF support.

 

Conclusion

One of the key ideas behind JSF is the separation of the visual presentation and the application logic. Admittedly, older versions of JSF have not always made this separation easy or intuitive, but JSF 2.0 changes that. With JSF 2.0, you should be able to write your bean classes without using the JSF API, leaving that API for authoring components. And, if you use composite components, you may never need to see the JSF API at all!

Related Topics >>

Comments

SelectItems question, is a Converter needed for binding to compl

One thing that I've been wondering for awhile... In your POJO example for SelectItems, you show the itemValue being set to the id property of a ToppingItem. What if you want to set the value to the ToppingItem itself. Do you need to use a Converter for that? I've always had to use a Converter in the past, but, it seems like it shouldn't be necessary. So, I'm wondering if I'm doing something wrong, or, if that's just a limitation of JSF. It seems like if the conponent is bound to a List in a session bean, that it should just use the item's value and shouldn't have issues converting things to Strings. The fact that it seems to want to call the object's toString() is quite annoying.

I found that the following code won't work for me unless I use a Converter.

h:selectOneMenu id="courseSelectOneMenu" value="#{editCourse.course}"
f:selectItems value="#{editCourse.courses}" var="c" itemLabel="#{c.name}" itemValue="#{c}"
h:selectOneMenu

Note, I couldn't put the JSF code in properly since this site has completely retarded input requirements. Have they not heard about escaping user input in input fields???

Another thing is...

I think they should add a property that adds a blank line to selectOneMenu which is selected if the user hasn't chosen a value. It would map to null. Otherwise, anytime you don't have an item selected by default, you have to copy the list to another list and add a blank item. This is something that's always annoyed me about JSF. There are certain things that are done on a regular basis that are more difficult than they should be. If there's no way to bind to a complex object and have that set the selected value, I think that's a design flaw (it wouldn't be the first one).

DataTables without using DataModel

Cay, I just tried your suggestion about passing the RowData to the method to determine the currently selected row in the DataTable. It's not working though. I'm using JSF 2.0 (Mojarra 2.0.2 (FCS b10)) with Tomcat 6.0.20 and NetBeans 6.8. I'm receiving the following error. It looks like it doesn't like the EL. Note, "s" is what I have the var set to in the DataTable. /editBasicSentencesSection.xhtml @130,54 action="#{editBasicSentencesSection.editSentence(s)}" Error Parsing: #{editBasicSentencesSection.editSentence(s)} One thing that I didn't do is use the @Named annotation on the class since I'm not using a full blown JEE 6 container. Could that be the problem? I hope I can get this to work with just Tomcat because I ABOLUTELY HATE using DataModel and was ecstatic when I read your post about a way to not have to use it. That class has always irritated the hell out of me. Also, I'm using Facelets. Not sure if that would make a difference. Also, I noticed that in the PDL documentation for the action attribute on h:commandLink, it says the method has to be a no arg method. So, it's not even documented, if it does work. I can't wait for your new book because the official JSF documentation seems to pretty much suck unless I'm missing something. Jon

I think you need EL API 2.2 for method expressions with params

Tomcat 6.0.20 ships with EL API 2.1. What is it with the Tomcat folks that Tomcat is always so out of date anymore?

You need to add the current

You need to add the current EL library--see http://weblogs.java.net/blog/cayhorstmann/archive/2009/12/29/jsf-20-and-... for instructions.

How badly do you need to use Tomcat? If you use GlassFish, you get one-stop shopping for the latest Java EE features. And it's fast and easy to administer.

Thanks for the info

I've been thinking about switching to Glassfish, but, there are a few things that I've been meaning to figure out. One thing that I want to figure out is how to deploy to it (without having to do file copies or manually use the web app). With Tomcat, I've been using the Ant deployer task. I don't know how to do that with Glassfish. The other issue is that I'm not 100% sold on the implementations that are included with Glassfish. e.g. say I want to use Hibernate's implementation of JPA instead of EclipseLink. Can I swap that out easily? EclipseLink seems to be quite fickle and I've had issues with the cache getting corrupted and strange behavior occurring. I've been able to use Hibernate in a more reliable manner. Also, TopLink's connection pool seemed to have pretty idiotic handling of timeout/broken connections. It wouldn't automatically remove them from the connection pool and would propagate the error through to the front end whereas Hibernate would handle that automatically (as IMHO it should). The documentation for the projects on java.net seems to suck a lot worse than Hibernate's documentation also. So, basically, while it's nice that everything is included by default. There may be times that I want to use something else. And I'm not sure how easy or difficult it would be to switch. Regarding the updated EL, I'm just going to wait for Tomcat 7 for which a first version is supposedly due out this month. I definitely hear what you're saying though about how much of a pain it is to get all this stuff installed and running on Tomcat. I don't know if it's politics or what or why Tomcat seems to be lagging and falling behind.

 There is an Ant task for

 There is an Ant task for deploying that, frankly, I haven't used for a couple of years because hot deployment from the IDEs is so good. I only run the Ant script for final testing, and in my script I now just copy the WAR into the autodeploy directory. One less Ant plugin to worry about...

I have not had problems with Toplink myself, but Hibernate is certainly a great JPA implementation. Here are the instructions: 

https://www.hibernate.org/374.html

Thanks, got the new EL working with Tomcat 6

Thanks, I think I have the new EL working with Tomcat 6. Being able to pass parameters is great. I always wished it was possible to do that previously.

One other thing that I've been doing that I'm wondering if there is an easier way to do it is setting variables in one bean from another. I've been using code like the following. I'm wondering if there's an easier way to do this. Also, what I've been doing is making it so that I typically have one bean for each page just to keep things organized. I'm wondering if this is typically what other people do? Basically, I'm treating it kind of like ASP.NET where each page has a "code behind file" which is the backing bean.

public String editSentence(BasicSentence sentence) {
FacesContext fc = FacesContext.getCurrentInstance();
EditBasicSentence ebs = fc.getApplication().evaluateExpressionGet(fc, "#{editBasicSentence}", EditBasicSentence.class);
ebs.setSentence(sentence);
return "editBasicSentence";
}

It seems like the naming of a lot of the API methods are overly wordy and the API in general seems more complicated than necessary. This is one of the differences that I've noticed between Java and .NET. The most horrendous class in Java that I've run across to date would have to be XMLGregorianCalendar. You have to use a factory method to create instances of a date. And to top that off, it throws a checked exception even though it has to do with a config file and is for all practical purposes a non-recoverable error. I mean, is it really necessary to pluggable date classes? Seems like design pattern overkill to me. The fact that it doesn't just use a normal Date is pretty dumb also IMHO.

I won't take the bait on the

I won't take the bait on the date classes :-) 

Yes, this is an awful part of the API, and my rule of thumb is that if I find myself getting into awful API verbiage, then it is a signal that I am doing it wrong. In this case, the right way of solving the bean dependency issue is through resource injection, either with @Inject (in CDI) or with @ManagedProperty. 

@Inject BasicSentence sentence; 

or

@ManagedProperty("#{editBasicSentence}") BasicSentence sentence;

NB. There are a couple of areas in JSF where you cannot avoid awful API verbiage. Many of them have to do with resource handling and i18n. As a rule of thumb, if what you are trying to do is also done in com.sun.faces.util.Util, you need to write your own helper method. I have been waiting for years for someone in the expert group to look at that class for hints on API design flaws...

 

Message

Is there a workaround to avoid the use of javax.faces.application.FacesMessage and javax.faces.context.FacesContext for sending message?

Where are you sending the

Where are you sending the message from?

If you do it in a converter, write a custom converter instead of using a converter method. If you do it in a validator, consider using bean validation instead. That's a much nicer mechanism, and JSF 2.0 picks up the message just fine. 

If you do it in an event handler, then it isn't entirely unreasonable to require coupling to the API. After all, an event handler could do anything, and if it wants to set a message, it needs to use the faces context. Your bigger grief is the incredibly poor support for message bundles in the JSF API. 

About javax.faces.application.FacesMessage

Thank you for your suggestion. In my case, it was from an Event Handler. But maybe it's a functional error to use it there, since you loose the functional context of the information using a generic message class.

another UIComponent issue

HEy Cay,
when using backing beans, with session scope, using UIComponent as their porperties is an issue too, as they don't implement Serializable interfaces:
See here https://issues.apache.org/jira/browse/TRINIDAD-1676

Neither does ListDataModel

Idiotically, even ListDataModel isn't Serializable.