gkari
gkari

Reputation: 229

CDI: Property injection issue due to multiple inheritance and Generics abstraction

We use CDI for Dependency Injection. We need to implement a generic class, LazyAccountDataModel which will be used/injected by all data table beans (like in DataTableBean below). The generic LazyAccountDataModel needs to deal with a specific type of facade depending on the data table bean injecting it. I tried using Generics as an approach to determine which facade to use in the LazyAccountDataModel as follows but it throws the following exception:

@Named
@RequestScoped
public class DataTableBean
{
    @Inject
    private LazyAccountDataModel<IAccount, IAccountFacade> lazyModel;
}

@Named
@RequestScoped
public class LazyAccountDataModel<DO extends IDomainObject, FACADE extends
IPersistableFacade<DO>> extends LazyDataModel<DO>
{
    @EJB (@Named doesn't work here due to WELD bug GLASSFISH-16186 which is not-optimal)
    private FACADE facade;

    private List<DO> datasource;

    @Override
    public List<DO> load(int first, int pageSize, String sortField, SortOrder 
        sortOrder, Map<String, String> filters)
    {
    setRowCount((int) facade.findTotalCount());

    // do more work on specific facade derivation (IAccountFacade in this case)

    return datasource;
    }
}

public interface IAccountFacade extends IPersistableFacade<IAccount>
{
    public void logIn(String userName);
}

public interface IPersistableFacade<DO extends IDomainObject> extends IFacade<DO>
{
    void create(DO domainData);

    List<DO> getAll();

    long findTotalCount();
}

The exception stack trace

SEVERE: Exception while loading the app : WELD-001408 Unsatisfied dependencies for type [LazyAccountDataModel<IAccount, IAccountFacade>] with qualifiers [@Default] at injection point [[field] @Inject private view.dashDOard.DataTableBean.lazyModel]
    org.jDOss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [LazyAccountDataModel<IAccount, IAccountFacade>] with qualifiers [@Default] at injection point [[field] @Inject private view.dashboard.DataTableBean.lazyModel]
        at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:270)
        at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:106)
        at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:129)
        at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:351)
        at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:336)
        at org.jboss.weld.bootstrap.WeldDOotstrap.validateBeans(WeldDOotstrap.java:396)
        at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:190)
        at org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:128)
        at org.glassfish.internal.data.ApplicationInfo.start(ApplicationInfo.java:306)
        at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:462)
        at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:240)
        at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:382)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$1.execute(CommandRunnerImpl.java:355)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:370)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1064)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1200(CommandRunnerImpl.java:96)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1244)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1232)
        at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:459)
        at com.sun.enterprise.v3.admin.AdminAdapter.service(AdminAdapter.java:209)
        at com.sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:168)
        at com.sun.enterprise.v3.server.HK2Dispatcher.dispath(HK2Dispatcher.java:117)
        at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:238)
        at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:828)
        at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:725)
        at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1019)
        at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:225)
        at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
        at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
        at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
        at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
        at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
        at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
        at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
        at java.lang.Thread.run(Thread.java:662)

Our problem is quite similar to the following post: CDI producer method for data model. Can anyone provide pointers on how we can inject LazyAccountDataModel and specify to it which facade needs to be used?

One possible solution would be to inject the specific facade type along with the LazyAccountDataModel and then set the facade type explicitly. However, this is not a clean solution:

@Named
@RequestScoped
public class DataTableBean
{
    @EJB
    private IAccountFacade facade;

    @Inject
    private LazyAccountDataModel<IAccount> lazyModel;

@PostConstruct
public void init()
{
    // would rather this stayed decoupled/handled by IoC framework
    lazyModel.setfacade(facade);
}


@Named
@RequestScoped
public class LazyAccountDataModel<DO extends IDomainObject> extends LazyDataModel<DO>
{
    private IPersistableFacade<DO> facade;

    private List<DO> datasource;

    @Override
    public List<DO> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters)
    {
            setRowCount((int) facade.findTotalCount());

         // do more work on specific facade derivation (IAccountFacade in this     case)
        return datasource;
    }

    public void setfacade(IPersistableBusinessObjectfacade<DO> facade)
    {
        this.facade = facade;
    }
}

Another solution may be to use contextual producers like: http://blog.frankel.ch/further-into-cdi But this doesn't seem to work for 2-deep layers of abstraction. This use case would be easily implemented in Spring by specifying nested properties in the context XML. Can anyone provide any inputs on how this can be done in CDI? Any help would be appreciated.

Thanks.

Upvotes: 2

Views: 2785

Answers (2)

win_wave
win_wave

Reputation: 1508

At first look, question why do you use @Named qualifier? If you have used it on a class, then you need to have at the injection point otherwise it will not resolve the dependency.

The second is from my experience I could say that CDI does not like generics, and not sure will you be able to make it work like that. But if you will do following, then it may work: Make this one as interface:

LazyAccountDataModel<DO extends IDomainObject, FACADE extends IPersistableFacade<DO>>

And make a class which implements:

LazyAccountDataModel<IAccount, IAccountFacade>

Then you can inject like as in your example.
I doubt that this part will ever work:

@EJB (@Named doesn't work here due to WELD bug GLASSFISH-16186 which is not-optimal)
private FACADE facade;

Upvotes: 0

LightGuard
LightGuard

Reputation: 5378

According to Pete Muir (if you're using Weld) this is a bug. See more at CDI events and generics

Upvotes: 3

Related Questions