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.
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 JSF project, we wrote our own PartialViewContext to listen to some events fired by pages beans:
#RequestScoped
public class OurPartialViewContext extends PartialViewContextWrapper
{
...
// called by cdi
#SuppressWarnings("unused")
private void listenForUpdate(#Observes OurRefreshEvent event)
{
...
And we wrote the matching factory, injecting it:
public class OurPartialViewContextFactory extends PartialViewContextFactory
{
#Inject
private OurPartialViewContext customPartialViewContext;
...
Problem is that in the newest versions of JSF, the empty constructor for PartialViewContextWrapper is deprecated, asking us to use another constructor with the wrapped object in parameter.
Currently, our PartialViewContext needs to be tied to the request scope, in order to be modified during the request by the observed events and to be used by a custom PartialResponseWriter we also wrote.
So our PartialViewContext currently both:
must have an empty constructor, as it is a #RequestScoped bean;
should not have an empty constructor, as it is deprecated for PartialViewContextWrapper which it inherits from.
How could we find a solution there?
We tried to remove it from the scope and build it in the Factory with a simple new OurPartialViewContext(), but then the #Observes methods are never called.
You are required to pass the wrapped instance into the constructor and to use getWrapped() over all place in delegate methods. Otherwise your application will most probably not work when you install other JSF libraries which also ship with their own PartialViewContext implementation such as OmniFaces and PrimeFaces. You would be effectively completely skipping the functionality of their PartialViewContext implementation. This mistake was previously observed in too many custom implementations of factory-provided classes. Hence the urge to mark the default constructor as #Deprecated so that the developers are forced to use the proper design pattern.
Your specific issue can be solved by simply refactoring the listenForUpdate() method into a fullworthy request scoped CDI bean, which you then inject in the factory who in turn ultimately passes it into the constructor of your PartialViewContext implementation.
Thus, so:
#RequestScoped
public class OurEventObserver {
public void listenForUpdate(#Observes OurRefreshEvent event) {
// ...
}
}
public class OurPartialViewContextFactory extends PartialViewContextFactory {
#Inject
private OurEventObserver observer;
public OurPartialViewContextFactory(PartialViewContextFactory wrapped) {
super(wrapped);
}
#Override
public PartialViewContext getPartialViewContext(FacesContext context) {
PartialViewContext wrapped = getWrapped().getPartialViewContext(context);
return new OurPartialViewContext(wrapped, observer);
}
}
public class OurPartialViewContext extends PartialViewContextWrapper {
private OurEventObserver observer;
public OurPartialViewContext(PartialViewContext wrapped, OurEventObserver observer) {
super(wrapped);
this.observer = observer;
}
// ...
}
Inside any of the overridden methods of OurPartialViewContext you can simply access the state of the observer, provided that the listenForUpdate() modifies some instance variables representing the state.
I have a helper class, which is neither a stateful managed bean, nor a stateless EJB, nor an entity mapped to a table via JPA or Hibernate. It is simply a collection of static methods that do simple things like return date formats and similar.
Given that, in order for a Java class to be visible inside a JSF, that class must be annotated in a way that the container assigns as visible to JSFs, is there a way to annotate a helper class that does not match any of the standard JSF visible categories so that it becomes visible? An alternative, of course, is to have a conduit method in the managed bean that passes the call from the JSF to the helper class but I prefer not to clutter my managed bean if I can call it directly from the JSF. I understand that doing this with a stateless EJB from a JSF would be considered an anti-pattern but the methods in the class I wish to use are all very simple and non-transactional.
Mark your class as #ApplicationScoped. Make sure it has a public no-arg constructor and that this class doesn't have state and its methods are thread safe.
E.g.
Managed bean (pure JSF)
//this is important
import javax.faces.bean.ApplicationScoped;
#ManagedBean
#ApplicationScoped
public class Utility {
public static String foo(String another) {
return "hello " + another;
}
}
CDI version
//this is important
import javax.enterprise.context.ApplicationScoped;
#Named
#ApplicationScoped
public class Utility {
public static String foo(String another) {
return "hello " + another;
}
}
View
<h:outputText value="#{utility.foo('amphibient')}" />
I'm using JSF and am running in a problem for quite awhile, I've searched at a lot of places but couldn't find any suitable answer.
Can I have dependency injection working in an abstract (or more generally a class higher in the hierarchy) class ?
Also, how should we handle annotations when working with inheritance ? I've read that the common practice would be not to annotate the abstract class, only the concrete one, but then, it would imply no injection for that abstract ?
My problem is that one (check the last comment) :
Abstract class
#ManagedBean
#ViewScoped
public abstract class AbstractController<T extends VersionedObject> implements Serializable {
#ManagedProperty("#{libraryVersionController}")
private LibraryVersionController libraryVersionController;
public List<T> loadFromDatasource(IVersionedServiceBase<T> service) {
log.info("Loading generic VO from data source");
VersionedObjectFilter filter = new VersionedObjectFilter();
filter.setSelectedLibVersion(libraryVersionController.getSelectedItem());
// etc
}
// getters, setters...
}
Concrete class
#ManagedBean
#ViewScoped
public class DomainController extends AbstractController<Domain> implements Serializable {
private List<Domain> allItems;
private Domain[] selectedItem;
#ManagedProperty(value = "#{domainService}")
private DomainService domainService;
#PostConstruct
public void loadFromDatasource() {
allItems = super.loadFromDatasource(domainService);
// !! DOES NOT WORK, null pointer exception on abstract class (libraryVersionController)
// etc
}
Getters and setters are correctly set up, as I could read in my .xhml it is the concrete class that I'm referencing (#{domainController.allItems}), there is only one #PostConstruct. I'm using JSF2.1 and Mojarra.
Thanks for your help !
As to your NullPointerException, my guess is that AbstractController.setLibraryVersionController is missing. As I understand it, when the AbstractController is constructed (presumably it has an implicit constructor even though it's abstract), that method is needed to fill in the appropriate value.
I know you said all getters and setters are there, but this one seems tricky, so possibly you missed it. If you add logging to this method, you can check that JSF is attempting to fill in the value, and also check whether the value is null or not.
On the general question of how dependency injection works with the inheritance hierarchy, I would guess that your approach is OK, and that dependencies are injected for the base class and then for the derived class, down the chain.
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.