Tuesday, May 24, 2011

Using CDI Beans in your Faces Converter

Unfortunately, you cannot use @Inject within your FacesConverter. This becomes a problem when you want to convert, say, countries loaded from your database and cached in a context such as @ApplicationScoped. There are two answers here:

Option 1: If using JBoss
If you are using JBoss, just use Seam Faces. Just by including the library you can use the @Inject annotation, as well as @PostConstruct. Very Handy. If you are using Maven, just add this to your pom.xml:
<dependency>
<groupId>org.jboss.seam.faces</groupId>
<artifactId>seam-faces</artifactId>
<version>3.0.1.Final</version>
</dependency>
That is it! You can stop reading now :).

Option 2: If using GlassFish
Unfortunately, Glass fish 3.1 has a bug with the class loader, see:
Using Seam Faces exposes the bug. According to the bug report, 'you need to add dependencies to the classpath to satisfy any class that is referenced in a bean archive'. Not only annoying, but I couldn't get it to work even when dependencies where in the pom, it was one issue after another. Until GlassFish 3.1.1 I would recommend creating a simple Service Locator to lookup the CDI beans.

public class CDIServiceLocator {
    private static BeanManager getBeanManager() {
        try {
            InitialContext initialContext = new InitialContext();
            return (BeanManager) initialContext.lookup("java:comp/BeanManager");
        }
        catch (NamingException e) {
            throw new RuntimeException("Couldn't find BeanManager in JNDI");
        }
    }

    public static Object getBeanByName(String beanName) {
        BeanManager bm = getBeanManager();
        Set beans = bm.getBeans(beanName);
        if (beans == null || beans.isEmpty()) {
            return null;
        }
        Bean bean = beans.iterator().next();
        CreationalContext ctx = bm.createCreationalContext(bean);
        Object o = bm.getReference(bean, bean.getClass(), ctx);
        return o;
    }
}

You can then use the ServiceLocator to lookup beans like so:
countries = (List<country>) CDIServiceLocator.getBeanByName("countries");

This is a hack, no doubt about it. But until GlassFish 3.1.1 comes out, I believe its the best option - unless you can switch to JBoss. Big thanks to dominickdorn.com whose code and info this is largely based from.

No comments: