Does accessing the RequestContext from ApplicationScoped bean need synchronization? - jsf

I have two questions:
Is it legal to access the RequestContext from PrimeFaces inside an #ApplicationScoped bean?
Must the access to the RequestContext be synchronized?
Backing bean
#ApplicationScoped
public class MyDatabaseConnection
{
public void do()
{
RequestContext ctx = RequestContext.getCurrentInstance();
// ...
}
}
index.xhtml
<p:commandButton actionListener="#{myDatabaseConnection.do}"/>

Is it legal to access the RequestContext from PrimeFaces inside an #ApplicationScoped bean?
Yes, as long as you don't assign it as an instance variable (property) of the application scoped bean.
Note that the same holds true for FacesContext, ExternalContext, HttpServletRequest, HttpServletResponse, HttpSesison, etc..etc.. They are all not application scoped and can thus cause major trouble when being assigned/shared as a property of an application scoped bean.
I must admit that your chosen bean class name MyDatabaseConnection really scares me in combination with #ApplicationScoped annotation. A database connection absolutely isn't intented to be application scoped. See also among others: Is it safe to use a static java.sql.Connection instance in a multithreaded system?
Must the access to the RequestContext be synchronized?
No, you don't need to. It's in the current code example been obtained in the method local scope and not assigned as a property of the bean.
The following answers should be helpful in understanding the meaning of "request", "session" and "application" scopes:
How do servlets work? Instantiation, sessions, shared variables and multithreading
How to choose the right bean scope?

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

Getting application scoped bean in JSF 2.2 custom component

I am developing a new JSF 2.2 application.
I have an eagerly created, application scope managed bean that loads up some configuration data from an external file, at startup, and stores it as state.
I have a FacesComponent and FacesRenderer that I have working statically.
I would like to be able to get the configuration data stored in the managed bean into the FacesComponent. Is there a standard way to do this.
As far as I am aware, the managed bean cannot be injected into the component - is that correct?
I can try to get data into the custom component using attributes and el in the .xhtml file that uses the custom component e.g.
<my:customComponent data="#{managedBean.loadedData}"/>
but this seems like a really backwards way to do things and actually exposes internal implementation of the component to the component user.
Please let me know if there is another way, or if you need any more information.
Update: #BalsusC I have tried what you suggested
I have a loader that puts the loaded data into a holder object
#Named
#ApplicationScoped
public class Loader implements Serializable {
#Inject
private Holder holder
#PostConstruct
public void init() {
// Load data into the holder here
}
}
The holder is another application scoped bean
#Named
#ApplicationScope
public class Holder {...}
When loading the data the Holder instance is injected correctly into the Loader.
However when I they the following
#Named //Makes no difference if this is here or not
#FacesComponent(value="family", createTag=true, namespace="...namespace...", tagName="tag")
public class Component extends UIComponentBase {
#Inject
public class Holder holder;
#Override
public void encodeBegin(FacesContext context) {
holder.getData();
}
}
when the component comes to render, the holder is not injected and I get a null pointer exception. I have tried to do this with our without the #Named annotation with the same result.
Am I doing something wrong? Can you please advise.
Application scoped JSF managed beans are internally stored in the application map with the managed bean name as key.
So, the below inside any of UIComponent methods should do:
ManagedBean managedBean = (ManagedBean) getFacesContext().getExternalContext()
.getApplicationMap().get("managedBean");
// ...
This only makes the component tight coupled to the managed bean. I.e. the component can't exist without the managed bean. This should be clearly documented if the component is intented to be reusable in other webapps. Another option is to tie the managed bean exclusively to the component (perhaps in form of a composite component) and use another application scoped bean for "unrelated" application data.

Get new instance of session scoped bean with other name

I have a session scoped bean for a UI to edit some data. It is annotated with #Named and #SessionScoped and all runs in JBoss 6.2. Now I got the requirement for a nearly similar edit UI. The problem is that the two UIs can exist in parallel. So for a perfect reuse it would be nice to create a new instance of the bean with another name. Unfortunately I have no clue how to do this in a clean CDI way.
I don't like it so much to inherit from the bean and give another name. This was one of my ideas.
Another idea was to implement in the managed bean only the business logic and keep the data encapsulated from them and set the data object inside the managed bean when it is needed in the specific context. But maybe there is another CDI way with producers or something?
Changing the scope of the bean to ViewScope makes no sense in my case.
Thanks
Oliver
But maybe there is another CDI way with producers or something
Indeed, you could use a producer.
Kickoff example:
#SessionScoped
public class SessionBean {
#Produces
#Named("foo")
#SessionScoped
public SessionBean getAsFoo() {
return new SessionBean();
}
#Produces
#Named("bar")
#SessionScoped
public SessionBean getAsBar() {
return new SessionBean();
}
// ...
}
(method names are free to your choice)
Usage:
#Inject
#Named("foo")
private SessionBean foo;
#Inject
#Named("bar")
private SessionBean bar;

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

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

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.

Resources