Wednesday, April 13, 2011

Fixing EJB 3.1 + CDI + JSF 2.0 problem: javax.el.ELException: ...xhtml: The class ...WeldProxy does not have the property ...

Yesterday I got hit by a very frustrating problem in developing a Java EE 6 web application under Oracle GlassFish 3.1 Application Server, using EJB 3.1, CDI, and JSF 2.0 EL expression.

The error message:

SEVERE: Error Rendering View[/pages/refills.xhtml]
javax.el.ELException: /pages/refills.xhtml: The class 'com.abispulsa.bisnis.service.org$jboss$weld$bean-com$abispulsa$bisnis$jsf-SessionBean-RefillManager_$$_WeldProxy' does not have the property 'summary'.
at com.sun.faces.facelets.compiler.TextInstruction.write(TextInstruction.java:88)

It happens with this very simple JSF 2.0 Facelets tag:

<p>Records: #{refillManager.summary}</p>

Where refillManager is a EJB 3.1 bean:

import javax.ejb.Stateless;
import javax.inject.Named;

import com.abispulsa.refill.PendingHandler;

@Named("refillManager")
@Stateless
public class RefillManager implements PendingHandler {

Google Searches exposed many different issues, but nothing useful for my problem.

What I know is that it's caused by @Stateless, when I remove it and simply use CDI with @ApplicationScoped, it works perfectly. Of course at that point I lose declarative JPA transactions.
(note: You can use declarative transactions without EJB using Seam Persistence CDI extensions).

To add to the frustration, I can't seem to reproduce the problem with another EJB + CDI bean. The others work fine!

I found the solution while reading the excellent Java EE 6 Book Enterprise JavaBeans 3.1 from O'Reilly. It is @javax.ejb.LocalBean !

Cause of the problem is @Stateless/@Stateful session bean that implements an interface, but not marked it as @javax.ejb.LocalBean.

@LocalBeans designates that a session bean exposes a no-interface view.

If a bean implements interface(s), it seems EJB only exposes those interfaces, meaning JSF 2.0 EL will not be able to access getter/setters that are not part of the declared interfaces (although they're accessible using the concrete class).

This may also happen to objects from "rich" languages like Groovy or Scala.

Solution:

Annotate the class with @javax.ejb.LocalBean.

Example:

import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.inject.Named;

import com.abispulsa.refill.PendingHandler;

@Named("refillManager")
@Stateless
@LocalBean
public class RefillManager implements PendingHandler {

Alternatives:
  1. Use EJB + JSF ManagedBean in XML, not CDI.
  2. Use CDI + Seam Persistence / Solder, not EJB. (I guess prefer this one)
Also, when all else fails: Undeploy, restart GlassFish, redeploy.

If you want to develop enterprise web applications using the latest Java EE 6 and EJB 3.1 technology be sure to get Enterprise JavaBeans 3.1 book from O'Reilly.