ManagedBean injection out of JSF lifecycle [duplicate] - jsf

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.

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:
Backing beans (#ManagedBean) or CDI Beans (#Named)?

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.

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);

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;
}

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.

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.

Related

Getting JSF ServletContext in non-request environment in a CDI bean

I am using TomEE+ 1.7.1.
With JSF managed beans this code was working well:
#ManagedBean( eager = true )
#ApplicationScoped
public class AppBean {
#PostConstruct
public void init() {
ServletContext sc = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext();
if (GlobalSettings.TESTMODE) {
sc.getSessionCookieConfig().setDomain("." + GlobalSettings.APP_DOMAIN_TEST);
} else {
sc.getSessionCookieConfig().setDomain("." + GlobalSettings.APP_DOMAIN);
}
}
}
The init function ran at application startup and ServletContext was available.
I read everywhere that it's time to migrate to CDI beans instead of JSF beans. So I wanted to change #ManagedBean( eager = true ) to #Named #Eager (#Eager is from Omnifaces). Init function is running at application startup, but there is no FacesContext so I can't get ServletContext.
General question: How to get ServletContext in a non-request environment in CDI beans? (ServletContext is not a 'per request' object, so it should exist before the first request.)
Specific question: how to set the domain for the session cookies dynamically from code but before the first request occurs?
You should be using a ServletContextListener for the purpose of performing programmatic configuration on a servlet based application.
#WebListener
public class Config implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();
// ...
}
#Override
public void contextDestroyed(ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();
// ...
}
}
A #WebListener is inherently also CDI managed and thus you can just use #Inject and friends in there.
An application scoped managed bean is intented for holding application scoped data/state which can be used/shared across requests/views/sessions.
Per the CDI spec, you can #Inject the ServletContext into a CDI bean. Just be sure to do it in a #PostConstruct, as injected fields are available only after construction:
#Inject ServletContext extCtxt;
#PostConstruct
public void doSomething(){
// do something with your injected field
}

Is it possible to inject a view scoped bean into a #FacesValidator in JSF?

As the title implies, I'm trying to inject a view scoped bean into a validator decorated by #FacesValidator as follows.
#FacesValidator(value = "priceRangeValidator")
public final class PriceRangeValidator implements Validator {
#ManagedProperty(value="#{rangeSliderBean}")
private RangeSliderBean rangeSliderBean; //Setter only.
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
// The bean instance is unavailable here. It is null.
}
}
The target view scoped bean - RangeSliderBean is unavailable in the validate() method.
I'm temporarily following the following way to get an instance of that bean in the validate() method itself.
RangeSliderBean rangeSliderBean = context.getApplication().evaluateExpressionGet(context, "#{rangeSliderBean}", RangeSliderBean.class);
Is it possible to inject a view scoped JSF managed bean into a validator using the #ManagedProperty annotation or something else?
I'm using JSF 2.2.6.
UPDATE:
This does not work on Mojarra 2.3.0-m01. The bean instance still remains null as it did before. This time long after this post, I took a corresponding view scoped CDI bean (#ManagedProperty was replaced by #Inject).
Try dynamic Injection by evaluating an expression using Application like this.
FacesContext facesContext = FacesContext.getCurrentInstance();
RangeSliderBean rangeSliderBean = facesContext.getApplication().evaluateExpressionGet(facesContext, "#{rangeSliderBean}", RangeSliderBean .class);

JSF CDI #Named + isAnnotationPresent + #CustomSecurityAnnotation

I'm using a custom annotation security as described in this link. I'm upgrading my jsf managed beans from #ManagedBean to #Named (CDI). Since the CDI does not support the scope #ViewScoped, i'm using org.omnifaces.cdi.ViewScoped. However, in this case, this check fails if method.isAnnotationPresent(Permissao.class){}. How should I adapt my security check to use with CDI + ViewScoped from Omnifaces?
Edit
Using #ManagedBean/ViewScoped(jsf) instead of #Named/ViewScoped(cdi + omnifaces) works.
The problem occurs in this line if (metodo.isAnnotationPresent(Permissao.class)) {}
public void verificaPermissaoAcesso(ActionEvent event) {
final FacesContext facesContext = FacesContext.getCurrentInstance();
UIComponent source = event.getComponent();
ActionSource2 actionSource = (ActionSource2) source;
MethodExpression methodExpression = actionSource.getActionExpression();
String expressao = methodExpression.getExpressionString(); // #{nomeManagedBean.nomeMetodo(param)}
String nomeManagedBean = expressao.substring(0, expressao.indexOf('.')).substring(2);
String nomeMetodoComParenteses = expressao.substring(expressao.indexOf('.') + 1);
String nomeMetodo = nomeMetodoComParenteses.substring(0, nomeMetodoComParenteses.indexOf("("));
ELContext elContext = facesContext.getELContext();
ExpressionFactory factory = facesContext.getApplication().getExpressionFactory();
ValueExpression ve = factory.createValueExpression(elContext, "#{" + nomeManagedBean + '}', Object.class);
//Object jsfManagedBean = ve.getValue(elContext);
Context ctx = bm.getContext(org.omnifaces.cdi.ViewScoped.class);
Bean bean = bm.resolve(bm.getBeans(nomeManagedBean));
Object jsfManagedBeanProxy = ctx.get(bean);
List<Method> listaMetodos = Arrays.asList(jsfManagedBeanProxy.getClass().getMethods());
for (Method metodo : listaMetodos) {
if (nomeMetodo.equals(metodo.getName())) {
if (metodo.isAnnotationPresent(Permissao.class)) {
Permissao anotacaoSeguranca = metodo.getAnnotation(Permissao.class);
SegurancaUtil.verificar(anotacaoSeguranca.acoes());
break;
}
}
}
}
My annotation class
#Documented
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Permissao {
String[] acoes();
}
My annotated method in jsf managed bean
#Permissao(acoes={"permission1", "permission2"})
public void myMethod(long id) {}
Edit 2 - Solution by #meriton
Context ctx = bm.getContext(org.omnifaces.cdi.ViewScoped.class);
Bean bean = bm.resolve(bm.getBeans(nomeManagedBean));
Object jsfManagedBeanProxy = ctx.get(bean);
CDI doesn't inject a naked CDI bean as a dependency, but a proxy that redirects to the contextual object of the active scope. This proxy class does not have your annotation.
See https://issues.jboss.org/browse/CDI-10 for how to unwrap the proxy.

#PostConstruct not working in #FacesValidator

Is the post construct annotation not supported in validators?
I have a application scoped jndi servicelocator bean which I inject as a managed property into my validator.
#ManagedProperty(value = "#{jndiServiceLocatorBean}")
private final JndiServiceLocatorBean jndiServiceLocatorBean = null;
The post construct annotated method to initialize my necessary remote bean is never invoked and so my remote bean remains null.
private UserBeanRemote userBeanRemote = null;
#PostConstruct
public void postConstruct()
{
this.userBeanRemote = (UserBeanRemote) this.jndiServiceLocatorBean.getRemoteBean(UserBeanRemote.class);
}
It works only if the Validator is annotated as a #ManagedBean or #Named instead of #FacesValidator.
Just use the normal constructor instead.
#FacesValidator("fooValidator")
public class FooValidator implements Validator {
private UserBeanRemote userBeanRemote;
public FooValidator() {
FacesContext context = FacesContext.getCurrentInstance();
JndiServiceLocatorBean jndiServiceLocatorBean = context.getApplication().evaluateExpressionGet(context, "#{jndiServiceLocatorBean}", JndiServiceLocatorBean.class);
this.userBeanRemote = (UserBeanRemote) jndiServiceLocatorBean.getRemoteBean(UserBeanRemote.class);
}
// ...
}
Support for dependency injection in JSF artifacts other than #ManagedBean is planned for JSF 2.2 (spec issue 763).
See also:
How to inject in #FacesValidator with #EJB, #PersistenceContext, #Inject, #Autowired
Communication in JSF 2.0 - Getting an EJB in #FacesValidator and #FacesConverter

Get JSF managed bean by name in any Servlet related class

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.
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:
Backing beans (#ManagedBean) or CDI Beans (#Named)?
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.
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);
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;
}
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.
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.

Resources