Reputation: 229
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
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
Reputation: 5378
According to Pete Muir (if you're using Weld) this is a bug. See more at CDI events and generics
Upvotes: 3