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.
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;
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);
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 have the following classes:
#Named
#ViewScoped
public class BaseClass {
private SomeDependency dep;
public BaseClass(){}
#Inject
public BaseClass(SomeDependency dep) {
this.dep = dep;
}
#PostConstruct
private void initialize() {
dep.doSomething(); // Point "A"
}
public String getProperty() {
return "BaseClass-Property";
}
#Specializes
public class SpecialClass extends BaseClass() {
#Override
public String getProperty() {
return "SpecialClass-Property";
}
}
Now in some .xhtml I have something like
<h:outputText value="#{baseClass.property}" />
This works fine without the SpecialClass. It breaks with a NullPointerException at Point "A" if I include the SpecialClass in the project.
Well, according to the Weld specification, this is more or less intended behavior:
When an enabled bean specializes another bean, the other bean is never
instantiated or called by the container.
Nevertheless, now I have to make sure that every #Specializes bean implements the complete constructor like
public SpecialClass() {}
#Inject
public SpecialClass(SomeDependency dep) { super(dep); }
which IMHO is kind of counter-intuitive and produces a lot of duplicated, boilerplate code, especially with something like 5-6 arguments for every constructor. Also, this is never caught when creating a new specialized bean since the project is always still compile clean.
Am I doing something wrong or is there no alternative to implementing the constructors over and over again?
BTW, I do use Constructor Injection to create easily testable classes where I can just use the constructor to "Inject" dummy implementations of the dependencies.
CDI 1.1 spec at section 4.3 says:
"The only way one bean can completely override a second bean at all
injection points is if it implements all the bean types and declares
all the qualifiers of the second bean."
Your base class is annotated with the Named qualifier and the specializing class is not. You should also mark it with Alternative and enable it in beans.xml. There's also the ViewScoped annotation. Unless it's the Omnifaces' ViewScoped, it looks like you're mixing up JSF managed beans with CDI beans.
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;