Sunday, 6 June 2010

EL Coerces empty/null to ZERO

I recently came across a problem where I was expecting a null value to be populated into one of my JSF managed beans on submit. After digging through the entire stack, I found out this was deliberate.
This bug was introduced into Tomcat 6.0.16, as a consequence of implementing the EL spec to the letter.
The option to disable this change at command line was added in 6.0.17:
-Dorg.apache.el.parser.COERCE_TO_ZERO=false

A patch in Tomcat 6.0.24 was added to allow you to change it at run time:
System.getProperties().put("org.apache.el.parser.COERCE_TO_ZERO", "false");

This problem also directly occurs in JSF, as it uses an EL engine as well. There's a patch for 1.2_08-b01 but it is happening in 2.0.2. But the above fix seems to work.

Request to change the JSP Spec
As per chapter 1.18.2 and 1.18.3 of EL spec, EL coerces null values to non-null
values such as empty String or (Long) 0 or (Boolean) false. This behaviour is in
almost all cases unwanted when the type is not a primitive.
After some more digging also came across this discussion at stackoverflow.

Tuesday, 1 June 2010

A simple way to Inject Guice into JSF (Mojarra)

Previously I have been using GuiceSF to inject the JSF managed beans which extends the Mojarra's implementation of ELResolver (ManagedBeanELResolver). This was bit of a hack but it was relatively simple to implement.
In upgrading to newer version of JSF, I had to manually tweak it to keep it in line with the new features in the spec which took a fair amount of debug stepping. So when JSF 2 came out I looked around for a way to do it which still used the implementation's bean creation and handling but also injected it with Guice.

After digging around in the injection code in Mojarra, I saw how simple it would be to extend the provided Tomcat injection (which injects stuff for the @Resource and @EJB etc annotations).

This solution first requires the injection provider be specified in web.xml:
<context-param>
   <description>JSF Injection provider</description>
   <param-name>com.sun.faces.injectionProvider</param-name>
   <param-value>net.devgrok.jsf.Tomcat6GuiceInjectionProvider</param-value>
</context-param>

Next extend the Tomcat injector:
package net.devgrok.jsf;

import javax.faces.FacesException;
import javax.servlet.ServletContext;

import org.apache.AnnotationProcessor;

import com.google.inject.Injector;
import com.sun.faces.spi.InjectionProviderException;
import com.sun.faces.vendor.Tomcat6InjectionProvider;

/**
 * Based on {@link com.sun.faces.vendor.Tomcat6InjectionProvider}. Injects using Guice for JSF.
 * 
 * @author Chris Watts
 */
public class Tomcat6GuiceInjectionProvider extends Tomcat6InjectionProvider
{

 private ServletContext servletContext;

 public Tomcat6GuiceInjectionProvider(ServletContext servletContext)
 {
  super(servletContext);
  this.servletContext = servletContext;
 }

 public void inject(Object managedBean) throws InjectionProviderException
 {
  try
  {
   getProcessor().processAnnotations(managedBean);
   getInjector().injectMembers(managedBean);
  }
  catch (Exception e)
  {
   throw new InjectionProviderException(e);
  }
 }

 private AnnotationProcessor getProcessor()
 {
  return ((AnnotationProcessor) servletContext.getAttribute(AnnotationProcessor.class.getName()));
 }
 
 protected Injector getInjector()
 {
  Injector injector = (Injector) servletContext.getAttribute(Injector.class.getName());
  if (injector == null)
   throw new FacesException("Guice injector not found, verify your web.xml file for mapping GuiceContextListener");
  return injector;
 }
}