CDI/Weld manual lookup

21 Apr
2010

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

Avatar

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

Avatar

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.

Avatar

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.

Avatar

@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 […]

Avatar

CDI – DIY – Manual Injection | INsanityDesign

October 22nd, 2013 at 19:27

[…] thanks to Dominik Dorn for this life saving […]

Comment Form

top