In a Java EE 6, CDI 1.1.x, Seam 3 etc. environment, we need to find all CDI beans of the current view (#ViewScoped). What I have tried so far is using:
#Named
#ViewScoped
public class ViewHelper
{
#Inject
private BeanManager beanManager;
public doSomethingWithTheBeanInstances()
{
Set<Bean<?>> beans = this.getBeanManager().getBeans(
Object.class, new AnnotationLiteral<Any>(){}
);
// do something
...
}
}
However, this returns all beans it manages.
I need to find only those within the scope of the current view and - that would be ideal - only those that implement a specific interface (inherited over over multiple hierarchy levels).
What's the way to do it?
Note since CDI has no view scope, we're using Seam 3 to be able to annotate all our view-scoped beans like:
#Named
#ViewScoped
public class ResultManagerColumnHandler extends BaseColumnHandler
{
....
}
The above would be an instance to look for (the #ViewScoped is a CDI replacement by Seam 3).
How can it be done?
I am not familiar with Seam, but from CDI standpoint, this is what I would try. However, bean it mind that it will only work if beanManager.getContext(ViewScoped.class); returns a valid context instance for you:
#Inject
BeanManager bm;
public List<Object> getAllViewScoped() {
List<Object> allBeans = new ArrayList<Object>();
Set<Bean<?>> beans = bm.getBeans(Object.class);
// NOTE - context has to be active when you try this
Context context = bm.getContext(ViewScoped.class);
for (Bean<?> bean : beans) {
Object instance = context.get(bean);
if (instance != null) {
allBeans.add(instance);
}
}
return allBeans;
}
You also asked to only obtain beans that implement certain interface. For that, simply modify the code line retrieving all beans with desired type:
Set<Bean<?>> beans = bm.getBeans(MyInterface.class);
Related
I am using JSF 2.3 and I want to inject different sublaccess of a session scoped bean as a managed property using CDI.
From the link below
How to inject different subclasses as ManagedProperty JSF 2?
I understood that this was not possible for RequestScoped beans using the "old" JSF and JEE-annotations, but my beans have session scope and I am using CDI injection, and therefore different annotations than the ones used in the above link.
In particular I do have:
public abstract class BaseContainer {
String prop1;
}
#Named
#SessionScoped
public class MaklerContainer extends BaseContainer {
String prop 2;
}
#Named
#SessionScoped
public class AppManagerContainer extends MaklerContainer {
String prop 3;
}
public abstract class BaseBean {
#Inject
#javax.faces.annotation.ManagedProperty(value = "#{maklerSessionContainer}")
private MaklerSessionContainer maklerSessionContainer;
}
Is it possible to inject interchangeably instances of both MaklerContainer and AppManagerContainer as a managed property maklerSessionContainer of the class BaseBean above?
Let me describe one option, there may be others.
First of all, if you want to inject different sublaccess, you have to find a way to disambiguate them for CDI, or it will complain about "Ambiguous dependencies". E.g. given the class hierarchy of the question, the line below results in ambiguous dependency exception, because CDI cannot decide whether to inject the MaklerContainer or the AppManagerContainer that extends it:
#Inject MaklerContainer maklerContainer; // ambiguous!
You can use qualifiers, named beans, or #Typed (perhaps there are even more ways).
Let's use #Named, since it is already present.
The idea is to create a producer that introduces a third bean of type MaklerContainer, with a different name, to the appropriate scope. The producer code will decide which of the 2 implementations to choose at runtime. Something like this:
#ApplicationScoped
public class TheProducer {
#Inject #Named("maklerContainer")
private MaklerContainer maklerContainer;
#Inject #Named("appManagerContainer")
private AppManagerContainer appManagerContainer;
#Inject
private User currentUser;
#Produces
#SessionScoped
#Named("theOne") // choose appropriate name of course
public MaklerContainer makeMaklerContainer() {
if (currentUser.hasRole("Role1")) {
return appManagerContainer;
} else {
return maklerContainer;
}
}
}
Now all you have to do is inject the appropriate named MaklerContainer, like:
#Inject #Named("theOne") MaklerContainer maklerContainer;
I am new to jsf and using JSF 2.0 to keep user information in a session scoped bean. I need to access this information across other beans for grunt work. Presently, this is how i am doing:-
private UserBean myuser1 = (UserBean)FacesUtils.getManagedBean("UserBean");
and then access properties as
if (myuser1.getUserType == 1) ...
this works but some time throws Argument Error: parameter key is null exception. I have been using following method too:-
private UserBean myuser2 = new UserBean();
if (myuser2.getUserType == 1) ...
In second method, my understanding is that if UserBean is already created in session, it would be retried. There are lots of question about 'how to access one bean in another' so i am confused. Please tell me one clean method which should always work and not throw null pointer exception abruptly.
The simplest way I know of is using #ManagedProperty, I don't know what you mean by safest though.
Let's say this is your sessionScoped bean :
#ManagedBean
#SessionScopped
public class UserBean {
//bean attributes and methods
}
Then you can access it in any other bean (provided it has the same or a narrower scope) as an attribute like this :
#ManagedBean
#ViewScoped //in this cas you can use SessionScoped, FlowScoped, or RequestScoped too
public class AnotherBean {
#ManagedProperty("#{userBean}")
UserBean userB;
//rest of the bean
//be sure to add getters and setters for the injected bean
}
For more details check this
Hope this helps.
Actually,
parameter key is null exception: it's either you didn't initialize the object witch can be solver with either adding
object = new Object(); // in the constructor of the class.
The second problem may be that the object is " DETACHED " you need to call the object using the method merge (with the entity manager).
A detached object is a known value but the JPA system doesn't know if it is the latest version from the DB or even sometimes the id value is not set for some reason (Not managed with jpa in other words it can be your case).
If em is your entity manager and you have the following function:
public Object latestVersion(Object o){ em.merge; }
In your Bean with:
#EJB
Service service;
if you do em.latestVersion(o); the problem of detached object is solved.
And for the real answer:
To access a object from another view you can simply do the following.
#ManagedBean
#SessionScoped
..... Bean1 {
public static Object o;
.....
}
#ManagedBean
..... Bean 2 {
private Object b=Bean1.o;
.....
}
Good luck
The standard practice of setting dependency of a scoped bean in another scoped bean is to use #Inject annotation like
#Inject UserBean userBean; in the bean you want use the UserBean object.
Your UserBean should be a stateful one.
#Stateful
#LocalBean
public class UserBean
{
private String name;
public String getName() { return name; }
public void setName( String name_ ) { name = name_; }
}
And just inject it into a stateless bean to modify its state:
#Stateless
#LocalBean
public class MyStatelessBean
{
#EJB
private UserBean userBean;
public String getUserName() { userBean.getName(); };
public void setUserName( String name_ ) { userBean.setName( name_); }
}
Or you can access it from (not wider scoped) managed beans as well in the same way:
#ManagedBean
#Dependent
public class MyJSFManagedBean
{
#EJB
private UserBean userBean;
}
You wrote in your comment you does not use EJBs at all. The picture modify like this:
The UserBean should be a SessionScoped CDI bean
#Named
#SessionScoped
pubilc class UserBean
{}
The othe CDI bean should be in a nearer scope:
#Named
#Request // or #ViewScoped or #Dependent
public class OwnerBean
{
#Inject
UserBean userBean;
}
The container automatically takes care to create the beans in the right scope and insert them into the owers (any kind of container managed objects : servlets, filters, action listeners, JSF/CDI beans). You need to insert a wider scoped resource into a thinner scoped one.
I'm trying to use an application scoped bean in JSF2, but for some reason it is always null in my request scoped bean. Here's the code I'm using:
The application scoped bean:
#ManagedBean(eager=true, name="applicationTracking")
#ApplicationScoped
public class ApplicationTracking implements Serializable {
private static final long serialVersionUID = 4536466449079922778L;
public ApplicationTracking() {
System.out.println("ApplicationTracking constructed");
}
}
The request scoped bean:
#ManagedBean
#RequestScoped
public class SearchResults implements Serializable {
private static final long serialVersionUID = 4331629908101406406L;
#ManagedProperty("#{applicationTracking}")
private ApplicationTracking appTracking;
public ApplicationTracking getAppTracking() {
return appTracking;
}
public void setAppTracking(ApplicationTracking appTrack) {
this.appTracking = appTrack;
}
#PostConstruct
public void init() {
System.out.println("SearchResults.init CALLED, appTracking = " + appTracking);
}
}
According to everything I'm seeing in the forums this should work without any other configurations. When I start the server (Tomcat) I'm seeing the ApplicationTracking constructor and init methods getting called.
But in my SearchResults component the printout in the PostConstruct is always null:
SearchResults.init CALLED, appTracking = null
What am I missing?
Provided that you imported those annotations from the right package javax.faces.bean.*, then this problem will happen if you re-registered the very same managed bean class in faces-config.xml on a different managed bean name. Get rid of that faces-config.xml entry. That's the JSF 1.x style of registering managed beans. You don't need it in JSF 2.x. When you do so anyway, then it will override any annotation based registration on the managed bean class, causing them to be ineffective.
Make sure you don't read JSF 1.x targeted resources while learning and working with JSF 2.x. Many things are done differently in JSF 2.x.
I'm having troubles working with CDI in JSF project. I cannot force CDI to inject interface-based bean into JSF file.
#Named
public class ClassBasedNamedBean {
public String getMessage() {
return "Class-based Hello World!";
}
}
#Named
public interface InterfaceBasedNamedBean {
public String getMessage();
}
public class InterfaceBasedNamedBeanImpl implements InterfaceBasedNamedBean {
#Override
public String getMessage() {
return "Interface-based Hello World!";
}
}
I can use both beans in WebServlet environment:
#WebServlet("/HelloServlet")
public class HelloServlet extends HttpServlet {
#Inject
private ClassBasedNamedBean classBasedNamedBean;
#Inject
private InterfaceBasedNamedBean interfaceBasedNamedBean;
protected void doGet(...) ... {
PrintWriter pw = response.getWriter();
pw.println("classBasedNamedBean: " + classBasedNamedBean.getMessage());
pw.println("interfaceBasedNamedBean: " + interfaceBasedNamedBean.getMessage());
// Output:
// classBasedNamedBean: Class-based Hello World!
// interfaceBasedNamedBean: Interface-based Hello World!
}
}
But interfaceBasedNamedBean is not available in JSF page:
<p>ClassBasedNamedBean: #{classBasedNamedBean.message}</p>
<p>InterfaceBasedNamedBean: #{interfaceBasedNamedBean.message}</p>
Output:
<p>ClassBasedNamedBean: Class-based Hello World!</p>
<p>InterfaceBasedNamedBean: </p>
How could I fix this issue? Does JSF require some explicit configuration for interfaceBasedNamedBean?
As my best guess:
I don't think that this is mentioned in the spec, but I'm pretty sure that #Named is not intended to be used on interfaces.
After all, it's just a matching between a type and an EL-name - and it seems as if the EL resolver can't find anything (concrete) under the name of the interface.
So, try annotating the implementation, not the interface - this should work. If you need to be flexible with various implementations of the same bean type - inject it in a controller bean and make that bean accessible.
You set javax.enterprise.inject.spi.BeanManager as your BeanManager?
What happens if you add #Named("InterfaceBasedNamedBean") to your Class Definition and remove the annotation from the interface? For what reason are you using the annotations anyways? CDI is not requiring them in contrast to spring.
Have you tried using a producer method?
Try adding scope to your bean with #RequestScoped, for example. From Weld docs:
The #Named annotation is not what makes the class a bean. Most classes in a bean archive are already recognized as beans. The #Named annotation just makes it possible to reference the bean from the EL, most commonly from a JSF view.
CDI defines #Named as a String-based qualifier. A qualifier's purpose is to distinguish which implementation to use at the injection point. The #Named's javadoc gives an example:
public class Car {
#Inject #Named("driver") Seat driverSeat;
#Inject #Named("passenger") Seat passengerSeat;
...
}
Thus #Named must annotate a specific implementation.
#Named as a way to use CDI beans in JSF views can be seen as a secondary function. #Named is not "#Inject for JSF" as it may seem to be.
I'm probably really close to the solution but I'm new with JSF and I don't see my mistake.
I have a first SessionScoped Managed Bean that represents Business information (address, website, ...)
#Named(value = "businessController")
#SessionScoped
public class BusinessController implements Serializable {
private Business current;
#EJB private BusinessFacade ejbFacade;
....
I have a second SessionScoped Managed Bean that represents the logged in user
#Named(value = "loginController")
#SessionScoped
public class LoginController implements Serializable {
private Login current;
#EJB
private LoginFacade ejbFacade;
#ManagedProperty(value="#{businessController}")
private BusinessController businessController;
public BusinessController getBusinessController() {
return businessController;
}
public void setBusinessController(BusinessController businessController) {
this.businessController = businessController;
}
When a user logs in, I set the current attribute from the loginController
Depending on this current user, I want to set the business attribute from the businessController :
businessController.setCurrent(current.getBusiness());
My problem is that the businessController attribute is null !
I use NetBeans 7.0.1 and GlassFish 3.1
In debug mode, I can see a viewId variable with the value
>No current context (stack frame)<
Unfortunately it doesn't ring any bell to me.
Any help would be appreciated
Thanks
You are mixing JSF managed beans with CDI managed beans.
Your BusinessController is annotated with the CDI annotaion #Named but gets injected with the #ManagedProperty annotation (from JSF). CDI managed beans need to be injected with #Inject. No getter or setter needed in this case. If you tend to use CDI, make sure that you import the correct #SessionScoped:
CDI: javax.enterprise.context.SessionScoped
JSF: javax.faces.bean.SessionScoped
Try the following (After making sure to have the correct scope class imported):
#Inject private BusinessController businessController;