So, you’re ended up in a situation, where you are somewhere (e.g. a javax.faces.Converter) where you are unable to simple @Inject SomeClass ?
I had the problem, that I had a FacesConverter like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | import javax.annotation.ManagedBean; import javax.annotation.PostConstruct; import javax.enterprise.context.RequestScoped; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.FacesConverter; import javax.inject.Inject; @FacesConverter(forClass = AvailableCountry.class) @ManagedBean // does not help :( @RequestScoped // does not help :( public class AvailableCountryConverter implements Converter { @Inject AvailableCountryDao dao; @PostConstruct public void postConstruct() { System.out.println("calling postConstruct"); } public Object getAsObject(FacesContext facesContext, UIComponent component, String value) { if (value == null || value.length() == 0) { return null; } return dao.find(getKey(value)); } Long getKey(String value) { Long key; key = Long.valueOf(value); return key; } String getStringKey(long value) { StringBuffer sb = new StringBuffer(); sb.append(value); return sb.toString(); } public String getAsString(FacesContext facesContext, UIComponent component, Object object) { if (object == null) { return null; } if (object instanceof AvailableCountry) { AvailableCountry o = (AvailableCountry) object; return getStringKey(o.getCountry().getId()); } else { throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + AvailableCountry.class.getName()); } } } |
But my DAO was not injected, nor was the postConstruct method triggered by CDI.
Why?
Because the bean is not managed by CDI, not even when annotating it with @ManagedBean because it gets created by the JSF-Lifecycle and not by CDI.
Well.. but how to manually lookup a Bean with CDI / Weld?
First, you need to get the BeanManager. When you have a FacesContext (like in the converter above), you can get it like this:
1 2 3 4 5 6 | public BeanManager getBeanManager() { return (BeanManager) ((ServletContext) facesContext.getExternalContext().getContext()) .getAttribute("javax.enterprise.inject.spi.BeanManager"); } |
If you don’t have access to a FacesContext, ServletContext or similar, you can lookup the BeanManager through JNDI
1 2 3 4 5 6 7 8 9 10 | public BeanManager getBeanManager() { try{ InitialContext initialContext = new InitialContext(); return (BeanManager) initialContext.lookup("java:comp/BeanManager"); catch (NamingException e) { log.error("Couldn't get BeanManager through JNDI"); return null; } } |
After you’ve got your BeanManager, simply lookup your Bean like this:
(In my case, I wanted to lookup a bean with the type AvailableCountryDao)
Type-based CDI manual lookup
1 2 3 4 5 6 7 8 | public AvailableCountryDao getFacade() { BeanManager bm = getBeanManager(); Bean<AvailableCountryDao> bean = (Bean<AvailableCountryDao>) bm.getBeans(AvailableCountryDao.class).iterator().next(); CreationalContext<AvailableCountryDao> ctx = bm.createCreationalContext(bean); AvailableCountryDao dao = (AvailableCountryDao) bm.getReference(bean, AvailableCountryDao.class, ctx); // this could be inlined, but intentionally left this way return dao; } |
Thanks to my friend Lincoln Baxter, III for the snipped.
Name-based CDI manual lookup
1 2 3 4 5 6 7 8 | public Object getBeanByName(String name) // eg. name=availableCountryDao { BeanManager bm = getBeanManager(); Bean bean = bm.getBeans(name).iterator().next(); CreationalContext ctx = bm.createCreationalContext(bean); // could be inlined below Object o = bm.getReference(bean, bean.getClass(), ctx); // could be inlined with return return o; } |
So, now you’re able to manually lookup beans with CDI.
In case you have the same problem (with Converters/Validators) like I had above, checkout Seam Faces, where this problem already is fixed,
meaning your @ManagedBean annotated Converter/Validator is working as expected with @Inject, @PostConstruct & @PreDestroy 🙂
5 Responses to CDI/Weld manual lookup
Tom
September 1st, 2010 at 11:29
Hi
Great example. Is it supposed to work also in a simple JAR ?
I am trying to have one “persistence JAR” that contains my generic DAO (with an @PersistenceContext in it)
Injected (through @Inject) in another JAR.
On top of those JAR is a WAR, which has no idea of how the persistence works. (it uses entry points from the Core JAR to communicate with them)
The @Inject doesnt work with Glassfish 3.0.1, I get a WELD-001408 error (Injection point has unsatisfied dependencies)
Is your method supposed to work in this case ?
What would be the name of my “persistence bean” ?
thank you
Dominik Dorn
September 6th, 2010 at 17:13
It is supposed to work, but it actually doesn’t:
Glassfish/Weld still have some issues when it comes to splitting up stuff into jars.
Your best chance is the Glassfish mailing list, bugging those people to finally fix their App server by creating bug reports, requests etc.
Alan
September 26th, 2010 at 04:50
Wow. This seems like a major hole in the JSF 2.0 architecture. I want to create composite components backed by UINamingContainer objects. This is where the tag has a componentType attribute and you create a class with a @FacesComponent annotation.
That part is very useful part of JSF 2.0, but I need to get my CDI managed data access objects from there! So instead of a simple @Inject I have to boilerplate this ball-cracking code?
Well, let’s see if this works.
@Inject-ed value null in @FacesComponent | Unlimitedtricks
October 8th, 2013 at 04:10
[…] CDI injection via lookup, or switch to EJB and do the simpler EJB […]
CDI – DIY – Manual Injection | INsanityDesign
October 22nd, 2013 at 19:27
[…] thanks to Dominik Dorn for this life saving […]