Get JSF managed bean by name in any Servlet related class
Asked Answered
U

6

106

I'm trying to write a custom servlet (for AJAX/JSON) in which I would like to reference my @ManagedBeans by name. I'm hoping to map:

http://host/app/myBean/myProperty

to:

@ManagedBean(name="myBean")
public class MyBean {
    public String getMyProperty();
}

Is it possible to load a bean by name from a regular servlet? Is there a JSF servlet or helper I could use for it?

I seem to be spoilt by Spring in which all this is too obvious.

Unipod answered 13/4, 2010 at 20:53 Comment(2)
I'm not sure if you can use these new annotations outside JSF/EL, but I'd start by looking at the JSR 299 spec: jcp.org/en/jsr/detail?id=299Preclude
Other people having problems with similar issues can also check bpcatalog.dev.java.net/ajax/jsf-ajax (related to AJAX and request mapping/handling, not getting beans by name)Unipod
S
272

In a servlet based artifact, such as @WebServlet, @WebFilter and @WebListener, you can grab a "plain vanilla" JSF @ManagedBean @RequestScoped by:

Bean bean = (Bean) request.getAttribute("beanName");

and @ManagedBean @SessionScoped by:

Bean bean = (Bean) request.getSession().getAttribute("beanName");

and @ManagedBean @ApplicationScoped by:

Bean bean = (Bean) getServletContext().getAttribute("beanName");

Note that this prerequires that the bean is already autocreated by JSF beforehand. Else these will return null. You'd then need to manually create the bean and use setAttribute("beanName", bean).


If you're able to use CDI @Named instead of the since JSF 2.3 deprecated @ManagedBean, then it's even more easy, particularly because you don't anymore need to manually create the beans:

@Inject
private Bean bean;

Note that this won't work when you're using @Named @ViewScoped because the bean can only be identified by JSF view state and that's only available when the FacesServlet has been invoked. So in a filter which runs before that, accessing an @Injected @ViewScoped will always throw ContextNotActiveException.


Only when you're inside @ManagedBean, then you can use @ManagedProperty:

@ManagedProperty("#{bean}")
private Bean bean;

Note that this doesn't work inside a @Named or @WebServlet or any other artifact. It really works inside @ManagedBean only.


If you're not inside a @ManagedBean, but the FacesContext is readily available (i.e. FacesContext#getCurrentInstance() doesn't return null), you can also use Application#evaluateExpressionGet():

FacesContext context = FacesContext.getCurrentInstance();
Bean bean = context.getApplication().evaluateExpressionGet(context, "#{beanName}", Bean.class);

which can be convenienced as follows:

@SuppressWarnings("unchecked")
public static <T> T findBean(String beanName) {
    FacesContext context = FacesContext.getCurrentInstance();
    return (T) context.getApplication().evaluateExpressionGet(context, "#{" + beanName + "}", Object.class);
}

and can be used as follows:

Bean bean = findBean("bean");

See also:

Submediant answered 13/4, 2010 at 22:50 Comment(9)
You're second suggestion about just injecting the bean was so amazingly simple I had totally overlooked it. As always, your response is perfectly to the point. Thanks so much for your work here on SO.Somewise
In the meantime (speaking as of JSF 2.2) it seems like the method evaluateExpressionGet was extended with a third parameter that allows to specify the expected class so casting won't be necessary anymore. PostBean bean = context.getApplication().evaluateExpressionGet(context, "#{beanName}", PostBean.class);Deadandalive
@Marc: Has been in from the beginning. Was just a leftover from a copypaste mistake I guess. Answer has been corrected. Thank you for notifying.Submediant
FacesContext is available even though the static utility method findBean() is defined inside a plain Java class. How is it available there in a plain Java class which is not managed by JSF?Ben
@Tiny: it's in turn called by a JSF artifact within the same thread.Submediant
Hi I get FacesContext context = FacesContext.getCurrentInstance(); context=null why?? and Trying to use HttpServletRequest request like this Bean bean = (Bean) request.getSession().getAttribute("ingresoSistema"); I got this: Servlet.service() for servlet servletsimple threw exception java.lang.IllegalStateException: Cannot create a session after the response has been committed. My bean: @ManagedBean(name = "ingresoSistema") @SessionScopedMillner
@Submediant I was expecting findBean(String beanName) to be available in showcase.omnifaces.org/utils/Beans, but it's not. Is there any reason for that (other than keeping it strictly CDI)?Sialoid
@Jasper: You can use Faces#evaluateExpressionGet() for this.Submediant
@Submediant OK, but you would still have to use "#{" + beanName + "}", right? Or am I being lazy here? ;-)Sialoid
P
11

I use the following method:

public static <T> T getBean(final String beanName, final Class<T> clazz) {
    ELContext elContext = FacesContext.getCurrentInstance().getELContext();
    return (T) FacesContext.getCurrentInstance().getApplication().getELResolver().getValue(elContext, null, beanName);
}

This allows me to get the returned object in a typed manner.

Praise answered 10/12, 2012 at 18:10 Comment(1)
This is already covered by the currently accepted answer and even in a more convenient way (Class argument is namely unnecessary in this construct).Submediant
D
3

Have you tried an approach like on this link? I'm not sure if createValueBinding() is still available but code like this should be accessible from a plain old Servlet. This does require to bean to already exist.

http://www.coderanch.com/t/211706/JSF/java/access-managed-bean-JSF-from

 FacesContext context = FacesContext.getCurrentInstance();  
 Application app = context.getApplication();
 // May be deprecated
 ValueBinding binding = app.createValueBinding("#{" + expr + "}"); 
 Object value = binding.getValue(context);
Disguise answered 13/4, 2010 at 21:10 Comment(3)
This probably won't work in a regular servlet. The FacesContext is a per-request thread-local artefact set up by the JSF lifecycle (usually the FacesServlet).Preclude
ValueBinding is deprecated since JSF 1.2 over 4 years ago.Submediant
@BalusC: It shows how up to date I am lol. On a sidenote, using a search engine to research techniques is turning out to be counterproductive with all the old information out there. @McDowell: That actually makes sense. I'll do a test just to see what happens.Disguise
P
3

You can get the managed bean by passing the name:

public static Object getBean(String beanName){
    Object bean = null;
    FacesContext fc = FacesContext.getCurrentInstance();
    if(fc!=null){
         ELContext elContext = fc.getELContext();
         bean = elContext.getELResolver().getValue(elContext, null, beanName);
    }

    return bean;
}
Pilcher answered 29/7, 2014 at 19:50 Comment(2)
I try to to this from a servlet but it doesn`t work.Millner
What if I am inside a Quartz Job and I don´t have FacesContext available? How can I get a Managed Bean from the application context?Mablemabry
G
0

I had same requirement.

I have used the below way to get it.

I had session scoped bean.

@ManagedBean(name="mb")
@SessionScopedpublic 
class ManagedBean {
     --------
}

I have used the below code in my servlet doPost() method.

ManagedBean mb = (ManagedBean) request.getSession().getAttribute("mb");

it solved my problem.

Ghoul answered 27/6, 2016 at 11:36 Comment(2)
What kind of servlet do you use? mateMillner
It is HttpServlet.Ghoul
C
-1

I use this:

public static <T> T getBean(Class<T> clazz) {
    try {
        String beanName = getBeanName(clazz);
        FacesContext facesContext = FacesContext.getCurrentInstance();
        return facesContext.getApplication().evaluateExpressionGet(facesContext, "#{" + beanName + "}", clazz);
    //return facesContext.getApplication().getELResolver().getValue(facesContext.getELContext(), null, nomeBean);
    } catch (Exception ex) {
        return null;
    }
}

public static <T> String getBeanName(Class<T> clazz) {
    ManagedBean managedBean = clazz.getAnnotation(ManagedBean.class);
    String beanName = managedBean.name();

    if (StringHelper.isNullOrEmpty(beanName)) {
        beanName = clazz.getSimpleName();
        beanName = Character.toLowerCase(beanName.charAt(0)) + beanName.substring(1);
    }

    return beanName;
}

And then call:

MyManageBean bean = getBean(MyManageBean.class);

This way you can refactor your code and track usages without problems.

Cataldo answered 11/9, 2015 at 18:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.