Correct way of retrieving another bean instance from context [duplicate] - jsf

This question already has answers here:
Get JSF managed bean by name in any Servlet related class
(6 answers)
Closed 2 years ago.
We are using the following code to get the managed bean instance from the context.
FacesUtils.getManagedBean("beanName");
Is it the correct way of doing it?. If multiple users access the same bean what will happen?
How the bean instances are managed?

Since FacesUtils is not part of standard JSF implementation, it's unclear what it is actually doing under the covers.
Regardless, when you're already inside a managed bean, then the preferred way is to inject the other bean as managed property. I'll assume that you're already on JSF 2.0, so here's a JSF 2.0 targeted example.
#ManagedBean
#SessionScoped
public void OtherBean {}
#ManagedBean
#RequestScoped
public void YourBean {
#ManagedProperty("#{otherBean}")
private void OtherBean;
#PostConstruct
public void init() {
otherBean.doSomething(); // OtherBean is now available in any method.
}
public void setOtherBean(OtherBean otherBean) {
this.otherBean = otherBean;
}
// Getter is not necessary.
}
But when you're still on JSF 1.x, then you need to do it by <managed-property> entry in faces-config.xml as explained in this question: Passing data between managed beans.
If you happen to use CDI #Named instead of JSF #ManagedBean, use #Inject instead of #ManagedProperty. For this, a setter method is not required.
See also:
Communication in JSF2
Get JSF managed bean by name in any Servlet related class
Backing beans (#ManagedBean) or CDI Beans (#Named)?
As to your concern
If multiple users access the same bean what will happen? How the bean instances are managed?
They are managed by JSF. If a bean is found, then JSF will just return exactly this bean. If no bean is found, then JSF will just auto-create one and put in the associated scope. JSF won't unnecessarily create multiple beans.

Related

Managed Bean not being instantiated at #ApplicationScoped and eager=true

I have the following UserView class:
#ManagedBean(name="usersView", eager=true)
#ApplicationScoped
public class UserView
{
...
public void setUsers(...)
{
...
}
}
I then have another class, UserService which tries to access this bean and then call this method, as follows:
UserView usersView = (UserView) FacesContext.getCurrentInstance().getExternalContext().getSessionMap("usersView");
usersView.setUsers(...)
My issue is, is that usersView keeps coming back as null and hence the method cannot be called.
My end goal is to use this data in a PrimeFaces datatable, like so:
<p:dataTable var="user" value="#{usersView.users}" ...>
I have also tried changing the scope to SessionScoped, but it is still null and I cannot figure out why.
Any suggestions would be greatly appreciated.
The getExternalContext().getXxxMap().get("beanName") approach won't autocreate beans if they don't exist yet. Managed beans will only be autocreated when an EL expression referencing the bean is evaluated. Accessing the scope map doesn't do that. For that, use #ManagedProperty in the source bean instead. You can specify an EL expression in its value.
#ManagedProperty("#{userView}")
private UserView userView; // +setter (no getter required).
Note that this only works if the source bean has the same or a narrower scope than the target bean. If that's not the case, consider managing beans using CDI instead of JSF. You can then use #Inject instead of #ManagedProperty (which doesn't require a setter by the way). Moreover, #ManagedBean and friends is deprecated in upcoming JSF 2.3.
As to eager=true, this has only effect on #ApplicationScoped beans. See also the javadoc (emphasis mine).
...
If the value of the eager() attribute is true, and the managed-bean-scope value is "application", the runtime must instantiate this class when the application starts. This instantiation and storing of the instance must happen before any requests are serviced. If eager is unspecified or false, or the managed-bean-scope is something other than "application", the default "lazy" instantiation and scoped storage of the managed bean happens.
...
See also:
How to choose the right bean scope?
Difference between #ManagedProperty and FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("bean")
Get JSF managed bean by name in any Servlet related class
Backing beans (#ManagedBean) or CDI Beans (#Named)?

Inject CDI managed bean in SystemEventListener

I have used answer of How to disable elements from within a ViewHandler after jsf has embedded the composite component? to programmatically disable all components in all forms. A SystemEventListener catches and disables all components.
What I would like to add is getting some logic from a CDI bean. It appears that I cannot #Inject a CDI bean inside SystemEventListener. Is there some other mechanism to get logic from CDI beans? I have lots of components and it would be very time consuming to add disabled="#{bean.disabled}" to all components. As I understand this is the right approach to "batch disable" or I'm heavily mistaken here?
It seems that you're not using JSF 2.2 yet. Since that version, a lot of JSF artifacts have been made eligible for CDI injection, including SystemEventListener instances. See also What's new in JSF 2.2? - Issue 763. If you're running JSF 2.0/2.1 on a Servlet 3.0 capable container, then it should be a minimum of effort to upgrade to JSF 2.2.
If you can't upgrade for some reason, then you can always programmatically grab the CDI managed bean via JNDI. The CDI BeanManager instance is available at JNDI name java:comp/BeanManager. Once having it at hands, use the below getReference() utility method to obtain the reference of interest.
public static <T> T getReference(BeanManager beanManager, Class<T> beanClass) {
Bean<T> bean = resolve(beanManager, beanClass);
return (bean != null) ? getReference(beanManager, bean) : null;
}
public static <T> Bean<T> resolve(BeanManager beanManager, Class<T> beanClass) {
Set<Bean<?>> beans = beanManager.getBeans(beanClass);
for (Bean<?> bean : beans) {
if (bean.getBeanClass() == beanClass) {
return (Bean<T>) beanManager.resolve(Collections.<Bean<?>>singleton(bean));
}
}
return (Bean<T>) beanManager.resolve(beans);
}
public static <T> T getReference(BeanManager beanManager, Bean<T> bean) {
return (T) beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean));
}
(source code from OmniFaces Beans/BeansLocal)
So, in a nutshell:
BeanManager beanManager = (BeanManager) new InitialContext().lookup("java:comp/BeanManager");
YourBean yourBean = getReference(beanManager, YourBean.class);
// ...
Or, if you're already using OmniFaces 1.x, or are open to using it, use its Beans utility class (available since 1.6 only):
YourBean yourBean = Beans.getReference(YourBean.class);
// ...
Both return a proxy reference, you could safely assign it as an instance variable of the SystemEventListener class during its construction.

Injecting one view scoped bean in another view scoped bean causes it to be recreated

I need to use some data saved in a view scoped bean in an other view scoped bean.
#ManagedBean
#ViewScoped
public class Attivita implements Serializable {
//
}
and
#ManagedBean
#ViewScoped
public class Nota implements Serializable {
#ManagedProperty("#{attivita}")
private Attivita attivita;
// Getter and setter.
}
Now, maybe my theory about it is still quite poor, I have noticed that when #{attivita} is injected, the Attivita constructor is invoked and thus creating another instance. Is it the right behaviour? What about if I want to reference the same instance and not create a new one?
This will happen if you're navigating from one to the other view on a postback. A view scoped bean is not tied to a request, but to a view. So when you navigate to a new view, it will get a brand new instance of the view scoped bean. It won't reuse the same bean instance which is associated with a previous view.
I understand that the attivita bean is created on the initial view and reused on postback. I understand that nota bean is associated with the new view where you're navigating to. When injecting attivita in it, it will simply get a new and distinct instance even though there's another instance in the very same request. This is all expected (and admittedly a bit unintuitive) behaviour.
There is no standard JSF solution for this. CDI solves this with #ConversationScoped (the bean lives as long as you explicitly tell it to live) and the CDI extension MyFaces CODI goes a bit further with #ViewAccessScoped (the bean lives as long as the navigated view references it).
You could however workaround this by storing the bean as an attribute in the request scope.
#ManagedBean
#ViewScoped
public class Attivita implements Serializable {
public String submit() {
FacesContext.getCurrentInstance().getExternalContext()
.getRequestMap().put("attivita", this);
return "nota";
}
}
and
#ManagedBean
#ViewScoped
public class Nota implements Serializable {
private Attivita attivita;
#PostConstruct
public void init() {
attivita = (Attivita) FacesContext.getCurrentInstance().getExternalContext()
.getRequestMap().get("attivita");
}
}
Note that this is rather hacky. There may be better solutions depending on the concrete functional requirement. Also note that you should in the nota view reference the desired Attivita bean instance as #{nota.attivita} and not as #{attivita}, because it would give you a new and different instance, for the reasons already explained before.
Your attivita bean is #ViewScoped and that doesn't guarantee that your instance will be hold in session. You need a #SessionScoped bean. However, if you need attivita for some reason to be #ViewScoped, then you can pass params through them in other ways, e.g. using viewParam or using other #SessionScoped bean between them.
Page Params
http://mkblog.exadel.com/2010/07/learning-jsf2-page-params-and-page-actions/
JSF 2 Managed Bean Scopes
http://balusc.blogspot.com.es/2011/09/communication-in-jsf-20.html#ManagedBeanScopes

get a backing bean property value from another bean

It is possible to access or to get a backing bean property value from another backing bean in JSF?
Yes, it is possible.
You can access another beans in the context by Application#evaluateExpressionGet(). You can also inject a bean as managed property of other bean in faces-config.xml. If you're already on JSF2, you can even use #ManagedProperty annotation like follows:
#ManagedProperty(value="#{bean}")
private Bean bean;

Inject a EJB into a JSF converter with JEE6 [duplicate]

This question already has answers here:
How to inject #EJB, #PersistenceContext, #Inject, #Autowired, etc in #FacesConverter?
(5 answers)
Closed 6 years ago.
I have a stateless EJB that acceses my database.
I need this bean in a JSF 2 converter to retreive an entity object from the String value parameter. I'm using JEE6 with Glassfish V3.
#EJB annotation does not work and gets a NPE, because it's in the faces context and it has not access to the EJB context.
My question is:
Is it still possible to Inject this bean with a #Resource or other annotation, or a JNDI lookup, or do I need a workaround?
Solution
Do a JNDI lookup like this:
try {
ic = new InitialContext();
myejb= (MyEJB) ic
.lookup("java:global/xxxx/MyEJB");
} catch (NamingException e) {
e.printStackTrace();
}
I never used JSF 2.0 (only 1.0), but chapter 5.4 of the spec says:
[...] allow the container to inject references to
container managed resources into a managed bean instance before it is made accessible to the JSF application.
Only beans declared to be in request,
session, or application scope are
eligble for resource injection.
But so far I understand, a JNDI lookup should do the trick.
Other (yet not so pretty) solution may be using binding instead of converterId. Using JSF managed beans only:
<f:converter binding="#{app.personConverter}" />
Where appBean stands for something like:
#ManagedBean(name="app")
#ApplicationScoped
class AppBean {
#EJB
private PersonService ps;
private Converter personConverter;
}
There MAY be a nicer solution in CDI-style (JSR-299) but i've failed to make this one running:
<f:converter binding="#{cdiBean}" />
Where cidBean ought to be:
#Named class CdiBean implements Converter { #EJB ... }
Fails with 'Default behavior invoked of requiring a converter-id passed in the constructor'
Anyhow first approach using binding and app scoped JSF bean works.
The Seam Faces extension for JSF 2.0 and CDI allows #Inject support directly in Validators and Converters.
Check it out: http://ocpsoft.com/java/seam-faces-3-0-0-alpha2-jsf-2-0-just-got-even-easier/
i don't know if this solution is pretty... but it does work:
#ManagedBean
public class AcquisitionConverter implements Converter
{
#EJB
private AcquisitionService service;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
...
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value)
{
...
}
}
and
<h:inputText value="#{flowController.acquisition}" converter="#{acquisitionConverter}">
with jsf 2.1.3 (mojarra) and glassfish 3.1.1

Resources