Using CDI instead of @ManagedBean: UnproxyableResolutionException because super class has no no-args constructor

cdijboss-weldjsf-2

I'm trying to use CDI for my JSF/Java EE application. I have the following class hierarchy:

/**
 * base controller class
 * also contains some final methods and an inner enum class declaration
 */
public abstract class AbstractCrudController<K, E> implements Serializable {
  private Class<E> entityClass;

  public AbstractCrudController(Class<E> entityClass) {
    this.entityClass = entityClass;
  }

  // ...
}


import javax.enterprise.context.SessionScoped;
import javax.inject.Named;

@Named
@SessionScoped
public class CategoryController extends AbstractCrudController<Long, Category> implements Serializable {
  public CategoryController() {
    super(Category.class);
  }
  //...
}

When I try to deploy the application on GF 3.1, I get the following CDI/Weld exception:

SEVERE: Exception while loading the
app : WELD-001435 Normal scoped bean
class
com.web.AbstractCrudController
is not proxyable because it has no
no-args constructor.
org.jboss.weld.exceptions.UnproxyableResolutionException:
WELD-001435 Normal scoped bean class
com.web.AbstractCrudController
is not proxyable because it has no
no-args constructor.
at org.jboss.weld.util.Proxies.getUnproxyableClassException(Proxies.java:215)
at org.jboss.weld.util.Proxies.getUnproxyableTypeException(Proxies.java:166)
at org.jboss.weld.util.Proxies.getUnproxyableTypesException(Proxies.java:191)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:134)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:148)
at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:363)
at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:349)
at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:416)
at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:178)
at org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:128)
at org.glassfish.internal.data.ApplicationInfo.start(ApplicationInfo.java:265)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:402)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:221)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:351)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$1.execute(CommandRunnerImpl.java:360)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:375)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1072)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1200(CommandRunnerImpl.java:101)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1221)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1210)
at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:375)
at com.sun.enterprise.v3.admin.AdminAdapter.service(AdminAdapter.java:209)
at com.sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:166)
at com.sun.enterprise.v3.server.HK2Dispatcher.dispath(HK2Dispatcher.java:117)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:234)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:824)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:721)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1014)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:220)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:530)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:511)
at java.lang.Thread.run(Thread.java:637)

Even if I add a no-args constructor to the base class, Weld still complains with the same exception that the class is not proxyable because it has final methods. Why does WELD force me to change my class design? Everything worked fine using the JSF @ManagedBean annotation.

I would appreciate any help.
Thank,
Theo

Best Answer

Why does WELD force me to change my class design? Everything worked fine using the JSF @ManagedBean annotation.

Well, Weld/CDI doesn't work the same way. My understanding is that when you use injection to obtain a reference to a bean, what you get is in most cases a proxy object. This proxy object sub-classes your bean and overrides the methods to implement delegation. And this introduces some restrictions on the classes CDI can proxy.

The CDI spec puts it like this:

5.4.1. Unproxyable bean types

Certain legal bean types cannot be proxied by the container:

  • classes which don't have a non-private constructor with no parameters,
  • classes which are declared final or have final methods,
  • primitive types,
  • and array types.

If an injection point whose declared type cannot be proxied by the container resolves to a bean with a normal scope, the container automatically detects the problem and treats it as a deployment problem.

My suggestion would be to make the methods non final.

References

  • CDI Specification
    • Section 5.4. "Client proxies"
    • Section 5.4.1 "Unproxyable bean types"
    • Section 6.3. "Normal scopes and pseudo-scopes"
Related Topic