CDI/Weld manual lookup

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 🙂

Join the Conversation

5 Comments

  1. 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

  2. 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.

  3. 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.

Leave a comment

Your email address will not be published. Required fields are marked *