I want to give on-the-go scope to a pojo bean in CDI during injection.
I created a plain bean and injected the same as #javax.enterprise.context.ApplicationScoped in a #javax.faces.bean.ViewScoped Managed Bean like this:
#Inject
#ApplicationScoped
Pojo pojo;
// POJO Class
Class Pojo {
private String var;
public Pojo() {
}
public String getVar() {
return var;
}
public void setVar(String var) {
this.var = var;
}
}
The Pojo bean's populated values could not be restored in a new view bean when I inject using the same syntax.
But it works when I use #ApplicationScoped in the class declaration instead, followed by non-scoped injection, like this:
#ApplicationScoped
Class Pojo {
private String var;
Injection:
#Inject
Pojo pojo;
The former case gets resolved when I make a producer and qualifier but I feel this would be an overhead I should do without. Being new to CDI, I want to ask what I am really missing here.
Scope and Context management are a very powerful feature in CDI. It is also part of the business logic of the components (an #ApplicationScoped bean won't be developed the same way than a #RequestScoped), that's why the scope is link to bean definition.
An injection point is only a place where you consume a bean and not a place where you define it, so there is no way to define the scope of a bean at the injection point at spec level.
Now if you really want to use this feature you could develop a portable extension to add this possibility. But you'll probably have to work on a qualifier system as well since scope is not used in bean resolving process (i.e. 2 beans with the same type in different scope will be in conflict for a given injection point if they don't have specific qualifier).
Related
My environment: Java 7/JSF 2.1/PrimeFaces 6.1.
My goal: to have a certain page of my application instantiated many times, one for each browser tab, each one with a different context.
My problem: everytime I open a second browser tab requesting from the same url, but with different object id, the previous one is destroyed, so only one backing bean instance is kept alive.
How do I know that: In my backing bean I have one method annotated with #PosConstruct and other with #PreDestroy, so I can track the life cicle of the instances.
My backing bean is annotated as follows:
#ViewController
public class MyBackingBeanMB extends AbstractBackingBeanMB {
private static final long serialVersionUID = 1L;
// many fields and methods
}
The #ViewController annotation is provided by the application framework I have to use. Such an annotation is declared as:
#Named
#Controller
#Stereotype
#ViewScoped // For me, this should do the trick, but...
#Target(value={TYPE})
#Retention(value=RUNTIME)
#Inherited
public #interface ViewController {
}
Update 1:
The #Controller annotation is also provided by the framework I use and is declared as:
#InterceptorBinding
#Inherited
#Target({ TYPE, METHOD })
#Retention(RUNTIME)
public #interface Controller {
}
Any ideas of what could be wrong?
TIA.
After some digging in the Internet I found Apache DeltaSpike, which provider a new kind of managed bean scope, WindowScoped.
Managed beans annotated with #WindowScoped` operate just like I wanted, providing me with the exact behaviour I needed and it is absolutely compatible with the framework I have to use.
In CDI, I am able to inject a bean with a particular scope, the scope with which the bean class was defined. But what if I create the bean class without any scope and I give scope to that bean at the point of injection. My requirement is to make injection-time-scoping possible in the latter case. Problem is that injection is happening with dependent scope instead of the desired annotated scope unless I use a producer.
For Example:
CASE 1:
When I declare scope of a bean in its class declaration like this:
#ApplicationScoped
class UserDetails {
...
}
And injected like this:
#ViewScoped
class UserView {
#Inject
UserDetails userDetails;
.
.
}
It works as expected. The bean injected in application scope is available throughout the application in all other beans.
CASE 2:
But when I give no scope in class declaration:
class UserDetails {
...
}
And injected like this (giving scope at the point of injection):
#ViewScoped
class UserView {
#Inject #ApplicationScoped
UserDetails userDetails;
.
.
}
This failed!.. The injected bean did not inject in application scope but got dependent scope instead (View Scope in my case).
I had to create a Producer & a Qualifier where #Produces method is providing the bean in desired application scope. I feel this producer/qualifier extension turns out to be an overhead if I have to inject bean class UserDetails in application scope in this case.
Thing is, UserDetails class is part of a third party jar. This class does not have any scope declared and is a POJO. I can not change its source code.
Based on the above discussion, I have two questions:
Why someone would create bean classes defined with no scope when they know that the beans are to be injected under a particular scope? Would this practice do any good in terms of design?
As I do not have control over the source code of the bean classes and as they are not associated with any scope, Is producer/qualifier extension the only good way to inject such beans in the desired scope?
1. Object without scope defined - #Dependent used
CDI will treat Object wihout scope as #Dependent scope.
An instance of a dependent bean is never shared between different
clients or different injection points. It is strictly a dependent
object of some other object. It is instantiated when the object it
belongs to is created, and destroyed when the object it belongs to is
destroyed.
Beans with scope #Dependent don’t need a proxy object. The client
holds a direct reference to its instance.
Spring IoC dev: CDI #Dependent scope is similar to Spring IoC Prototype scope.
2. No #Produces & just use #Inject
CDI will create new instance of UserDetails for each injection (#Dependent scope). No sharing data here! You can't define scope as you did (when inject).
3. Use #Produces & use #Inject
You can control the scope of UserDetails object (ApplicationScoped, SessionScoped, or RequestScoped)
public class Producers {
#Produces #ApplicationScoped
public UserDetails createUserDetails() {
// Initialize UserDetails
}
public void release(#Disposes UserDetails userDetails) {
// Release userDetails if you have to
}
}
4. Another way: Extend UserDetails if possible
#ApplicationScoped // Or #SessionScoped, #RequestScoped
public class UserDetailsImpl extends UserDetails {
//
}
If you want ApplicationScoped for UserDetails. Way 3 or way 4 can be used.
The scope is always defined on the bean, not on the injection point.
An injection point cannot change the scope of the injected bean.
I think you can inject an #ApplicationScoped bean as a #Dependent bean by specifying the #New annotation near the injection point, but you can't do the opposite.
I have a #SessionScoped cdi bean which is used to track user session information in my web application. Is there any way to find all objects of this bean from another #ApplicationScoped bean?
You cannot do this out of the box. Java EE forbid this kind of things for security reason.
Now you can imagine a more elaborate approaches to keep track of these session beans at your application scope level. The cleanest way would be to produce them from an #ApplicationScoped bean :
#ApplicationScoped
public class Registry {
private List<SessionData> data = new ArrayList<>;
#Produces
#SessionScoped
public SessionData produceSessionData() {
SessionData ret = new SessionData();
data.add(ret);
return ret;
}
public void cleanSessionData(#Disposes SessionData toClean) {
data.remove(toClean);
}
}
Note the #Dispose method which will be called when your produced bean has ended its lifecycle. A convenient way to keep your list up to date and avoid extra memory usage.
I instantiated a request bean from another request bean,
new LoginManager();
But the property which is annotated with #ManagedProperty doesn't get the value from the asked reference, only in case of instantiation through the above way. It just contains null, causing NPE later in code. Also #PostConstruct won't be invoked. Why is it so & how should I deal with this?
#ManagedBean(name = "loginManager")
#RequestScoped
public class LoginManager {
private String userid;
private String password;
#ManagedProperty(value="#{currentSession}")
private UserSessionManager userSession;
}
But userSession can't read from the session scoped bean when this bean was instantiated using: new LoginManager();
However I can read the value using FacesContext!
You should not manually instantiate (manage) beans using new operator. You should let JSF do the job of managing the beans and instead grab the JSF-managed (JSF-instantiated) instance.
Either by #ManagedProperty in the bean where you need it:
#ManagedProperty("#{loginManager}")
private LoginManager loginManager;
Or by invoking EL programmatically (which is pretty borderline in your particular case):
LoginManager loginManager = context.getApplication().evaluateExpressionGet(context, "#{loginManager}", LoginManager.class);
// ...
If you insist in instantiating and managing the bean yourself, you should do all dependency injections yourself, also invoking the #PostConstruct yourself, if any, and finally also putting the bean in the desired scope yourself. E.g.
LoginManager loginManager = new LoginManager();
loginManager.setUserSession(userSession);
// Now use reflection to find and invoke #PostConstruct method.
// Finally store in the map which conforms the bean's scope.
externalContext.getRequestMap().put("loginManager", loginManager);
This boilerplate is exactly what JSF is supposed to take away from your hands. Make use of it.
After I commit some data into the database I want my session beans to automatically refresh themselves to reflect the recently committed data. How do I achieve this when using managed session beans in JSF 2.0?
Currently I have to restart the web server in order for the sessions to clear and load anew again.
2 ways:
Put them in the view scope instead. Storing view-specific data sessionwide is a waste. If you have a performance concern, you should concentrate on implementing connection pooling, DB-level pagination and/or caching in the persistence layer (JPA2 for example supports second level caching).
#ManagedBean
#ViewScoped
public class FooBean {
// ...
}
Add a public load() method so that it can be invoked from the action method (if necessary, from another bean).
#ManagedBean
#SessionScoped
public class FooBean {
private List<Foo> foos;
#EJB
private FooService fooService;
#PostConstruct
public void load() {
foos = fooService.list();
}
// ...
}
which can be invoked in action method inside the same bean (if you submit the form to the very same managed bean of course):
public void submit() {
fooService.save(foos);
load();
}
or from an action method in another bean (for the case that your managed bean design is a bit off from usual):
#ManagedProperty("#{fooBean}")
private FooBean fooBean;
public void submit() {
fooService.save(foos);
fooBean.load();
}
This of course only affects the current session. If you'd like to affect other sessions as well, you should really consider putting them in the view scope instead, as suggested in the 1st way.
See also:
How to choose the right bean scope?