Get new instance of session scoped bean with other name - jsf

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;

Related

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.

what scope of a bean before conversation.begin method is called

I have a CDI bean:
#Named
#ConversationScoped
public class MyBean implements Serializable{
#Inject
private Conversation converstion;
private void conversationBegin() {
converstion.begin();
}
}
My question is:
what scope of this bean before conversationBegin() is called?
I think you're thinking about CDI wrong.
You hold a contextual reference to a bean with type MyBean. When you use it, a contextual instance is either created or obtained from the active conversation context and proxied by your contextual reference.
There is no change of scope. The only thing that changes over time is which context is active and whether there are instances already in the context or not.

Injecting a view scoped bean into another view scoped bean

A view scoped bean remains alive as long as the user interacts with the same view (or until it is navigated to a different view).
Suppose a view scoped managed bean is injected into another view scoped bean like so,
#ManagedBean
#ViewScoped
public final class SharableManagedBean implements Serializable
{
private static final long serialVersionUID = 1L;
#EJB
private SharableBean sharableService;
//...Do something.
}
#ManagedBean
#ViewScoped
public final class TestManagedBean implements Serializable
{
private static final long serialVersionUID = 1L;
#EJB
private TestBean testBean;
#ManagedProperty(value="#{sharableManagedBean}")
private SharableManagedBean sharableManagedBean ;
//... Do something with the injected bean.
}
In this case, is it necessary for the SharableManagedBean to have a view scoped bean?
What happens, if it is a request scoped bean (SharableManagedBean)? Is it initialized only once, when TestManagedBean comes into picture and destroyed, when TestManagedBean is destroyed?
Even it's technically possible to do that (JSF allows you to inject beans which are at the same or wider scope) I don't see the point of doing that with #ViewScoped beans. From my point of view, a well designed JSF web application should have a single #ViewScoped bean tied to each specific view. Then, how to solve your issue? You can do it in two ways:
If SharableManagedBean is a utility bean which contain static methods which are not tied to JSF, just define this class as abstract and statically invoke its methods everytime you need.
If SharableManagedBean itself has to be a managed bean which access the FacesContext and has common code that is shared along all the view beans, just create an abstract class and make your #ViewScoped beans extend it.
For your last question (SharableManagedBean being #RequestScoped), JSF doesn't allow you doing that. You'll get an exception because of trying to inject a narrower scoped managed bean.
According to the Oracle docs:
Another important point about managed beans referencing each other is that a managed bean can only refer to other beans provide their scope is equal or has a longer lifespan than the calling object.
Update
If using CDI, it's possible to inject a #RequestScoped bean into a #ViewScoped one too, using the Proxy pattern. Have it a look.

What makes a bean a CDI bean?

In the top answer to this question for example : Java EE 6 #javax.annotation.ManagedBean vs. #javax.inject.Named vs. #javax.faces.ManagedBean I read that:
To deploy CDI beans, you must place a file called beans.xml in a
META-INF folder on the classpath. Once you do this, then every bean in
the package becomes a CDI bean.
And also it is said that:
If you want to use the CDI bean from a JSF page, you can give it a
name using the javax.inject.Named annotation.
I have a sample code that goes like this:
#ManagedBean
#ViewScoped
public class SignUpPage {
private User user;
#PostConstruct
public void init() {
user = new User();
}
#Inject
private UserDao userDao;
// rest of the class
So as far as I understand, my bean is still a JSF Managed Bean, it is not a CDI bean(or is it?). By the way, I have a beans.xml in my WEB-INF folder.
And #Inject works just fine in here. Also, I can access the bean with EL just fine(which makes me think it is still a JSF Managed Bean)
The UserDao class looks something like this:
#Stateless
public class UserDao {
EntityManager em;
#PostConstruct
public void initialize(){
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Persistence");
em = emf.createEntityManager();
}
So, it is as far as I know an EJB..
So do I have any CDI beans in this example? How does #Inject work here?
Hope my question is clear, Regards!
By CDI specification, every JavaBean is a Managed Bean (do not confuse it with JSF #ManagedBean, this is a different one) in project where the beans.xml is present. So every class is also eligible for dependency injection. Note that default scope of this class is Dependent.

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

Resources