CDI and JSF aren't Best Friends Yet

Where I started banging my head into the table.

This specific issue has actually has jumped up and bit me a few times so I’ll just try explain the most recent one. Basically I have a new object that I am creating through a JSF form page, and that object needs to have a default value set at creation which is determined at runtime. For the sake of exposing this little issue lets assume that this value needs to be used multiple times during the request and thus we will make the producer @RequestScoped to avoid duplicate database calls.

To the code!

@ConversationScoped
    @Named
    public class CarPricerManager implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        @Inject
        @Active
        private PriceBook activePriceBook;
    
        private CarPricer carPricer;
    
        public CarPricer getCarPricer() {
            if(carPricer == null) {
    
                carPricer = new CarPricer();
                carPricer.setPriceBook(activePriceBook);
            }
            return carPricer;
        }
    }
    
    public class PriceBooks {
    
        @Produces
        @RequestScoped
        @Active
        public PriceBook findActivePriceBook() {
            return em.createQuery(.....).getSingleResult();
        }
    
        @Produces
        @RequestScoped
        @Named
        public List<PriceBook> priceBooks() {
            return em.createQuery(.....).getResultList();
        }
    }

Now lets show the JSF snippet to select our price book when creating our new CarPricer

<h:selectOneMenu value="\#{carPricerManager.carPricer.priceBook}" required="true"
        converter="PriceBookConverter">
        <f:selectItem itemLabel="Choose" />
        <f:selectItems value="\#{priceBooks}" var="_apb" itemLabel="\#{_apb.name}" />
    </h:selectOneMenu>

Where it goes wrong.

What we would expect to happen is the selectOneMenu would initially be selected with the value of our active price book. However this isn’t going to be the case because the injection of our active price book does not give us an object of PriceBook.java it gives us an proxy of PriceBook.java and the equals(...) that JSF is going to call will not return true.

Why it goes wrong.

The CDI EG has determined that there is not meaningful way to implement .equals() and .hashCode() on a proxy that can delegate to the underlying bean (See OWB-458 & WELD-695). What this means is that two proxies of the same underlying bean will be equal. However, two proxies to underlying beans which are equal according to .equals will not be equal.

How can we fix this?

One solution would be to execute the .equals() on the converted value if a converter exists. However, the JSF RI Mojarra feels (JAVASERVERFACES-2393) that any proxy must should delegate equals, hashCode and toString to the proxied instance per java.lang.reflect.Proxy. In the case of CDI the underlying bean can change during the lifecycle of the proxy and thus delegating isn’t realistic. This appears to be one of those places where the JSF and CDI expert groups need to step in and find a solution so that developers can enjoy consistent and predictable behavior in their applications.

A solutions that work now.

By making the bean with the producers @RequestScoped and storing the values in the bean after initial population we can have default dependent scoped producers which don’t produce proxies and hit the database only once.

@RequestScoped
    public class PriceBooks {
    
        private PriceBook activeBook;
        private List<PriceBook> books;
    
        @Produces
        @Active
        public PriceBook findActivePriceBook() {
        if (activeBook == null)
            activeBook = em.createQuery(.....).getSingleResult();
            return activeBook;
        }
    
        @Produces
        @Named
        public List<PriceBook> priceBooks() {
        if (books == null) {
            books = em.createQuery(.....).getResultList();
            return books;
        }
    }
tags: cdi jsf java
Cody Lerum - September 11, 2012

Fighting with JSF Request charset: JBoss or Tomcat

After just spending a good amount of time troubleshooting an issue with character encoding in JSF + JBoss AS7 I thought I would throw up a few notes on the subject as there are lots of scattered bits floating around but no real clear explanation. Hopefully this does more than contribute to the scattered bits :-)

First lets start off with a little background on how servers and clients determine how to encode data for transfer

  • Server response to Client:

    • Server Encode: When a server is sending a response to a client with JSF it encodes the response based on the defined encoding of the file which is usually set at the top of a JSF XHTML file <?xml version="1.0" encoding="UTF-8"?> The server will also include a Content-Type in the response header sent to the client.

    • Client Decode: When the client receives a response it checks the Content-Type defined in the response headers to decide which charset to use for decode. However If the Content-Type is not listed in the response header then the client may check the page meta tags for a hint or default to ISO-8955-1

  • Client Request (POST) to Server:

    • Client Encode: The client will encode it’s request based on the encoding of the response it received from the server.

    • Server Decode: This is where things get a little tricky.

      • The request received from the client will likely lack any hint as to how things were encoded.

        • It may have a Accept-Charset defined, but that is simply informing the server which charset’s are valid for it’s response encoding.

        • It will also have a Content-Type which reflects the enctype set on the form element submitting this post. By default it is application/x-www-form-urlencoded. Notice no reference to the charset this request was encoded in.

        • Some requests like ajax requests will look like this application/x-www-form-urlencoded;charset=utf-8; This does work and Tomcat/JbossWeb will decode as utf-8 but I have not figured out how to force this on a non-ajax h:form

      • The catch here is that if there is not a charset defined in the request Content-Type then the request parameters will be decoded using ISO-8859-1.

So you’ve configured for pages for UTF-8 and ensured that your server responses are including a charset in the Content-Type. Everything is great until you submit the form and your UTF-8 encoded client request is decoded as ISO-8859-1. Depending on the data your users submit you may not even notice this in your app for weeks/months/years/never. But for example if user may submit something like I’m and you will get I’m

Once you accept that this is how things work, there is an easy way to fix this by adding the following snippet to your web.xml

<web-app>
      ...
    
        <filter>
          <filter-name>forceUTF8CharSet</filter-name>
          <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
          <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
          </init-param>
        </filter>
    
        <filter-mapping>
          <filter-name>forceUTF8CharSet</filter-name>
          <url-pattern>*</url-pattern>
        </filter-mapping>
      ...
    </web-app>

This filter currently is in Tomcat 7.0.20 and up. If you are running JBoss 7 you can just grab the class from Github import it into your project. I’ve opened JBWEB-225 with JBoss Web to have this filter added.

tags: java jsf jboss
Cody Lerum - January 18, 2012

JSF 2.0 still lacking support for collections

Unfortunately the freshly minted JSF 2.0 spec still doesn’t support a generic Collection interface. Currently only a List is supported and while this does work for many situations, it does cause many issues when you are working with a full JavaEE stack since it may be necessary to use Set or SortedSet when working with JPA.

It hasn’t been used in the past, but it’s time for the users to start voting on java.net. So go there, register and throw some of your votes towards this painful issue.

tags: java jsf
Cody Lerum - January 04, 2010
About outjected.com

Outjected is an infrequent blog about the frequent issues and annoyances that pop up while coding.

These are living posts and will be updated as errors or improvements are found.
About Me
Google+