Inject CDI managed bean in SystemEventListener - jsf

I have used answer of How to disable elements from within a ViewHandler after jsf has embedded the composite component? to programmatically disable all components in all forms. A SystemEventListener catches and disables all components.
What I would like to add is getting some logic from a CDI bean. It appears that I cannot #Inject a CDI bean inside SystemEventListener. Is there some other mechanism to get logic from CDI beans? I have lots of components and it would be very time consuming to add disabled="#{bean.disabled}" to all components. As I understand this is the right approach to "batch disable" or I'm heavily mistaken here?

It seems that you're not using JSF 2.2 yet. Since that version, a lot of JSF artifacts have been made eligible for CDI injection, including SystemEventListener instances. See also What's new in JSF 2.2? - Issue 763. If you're running JSF 2.0/2.1 on a Servlet 3.0 capable container, then it should be a minimum of effort to upgrade to JSF 2.2.
If you can't upgrade for some reason, then you can always programmatically grab the CDI managed bean via JNDI. The CDI BeanManager instance is available at JNDI name java:comp/BeanManager. Once having it at hands, use the below getReference() utility method to obtain the reference of interest.
public static <T> T getReference(BeanManager beanManager, Class<T> beanClass) {
Bean<T> bean = resolve(beanManager, beanClass);
return (bean != null) ? getReference(beanManager, bean) : null;
}
public static <T> Bean<T> resolve(BeanManager beanManager, Class<T> beanClass) {
Set<Bean<?>> beans = beanManager.getBeans(beanClass);
for (Bean<?> bean : beans) {
if (bean.getBeanClass() == beanClass) {
return (Bean<T>) beanManager.resolve(Collections.<Bean<?>>singleton(bean));
}
}
return (Bean<T>) beanManager.resolve(beans);
}
public static <T> T getReference(BeanManager beanManager, Bean<T> bean) {
return (T) beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean));
}
(source code from OmniFaces Beans/BeansLocal)
So, in a nutshell:
BeanManager beanManager = (BeanManager) new InitialContext().lookup("java:comp/BeanManager");
YourBean yourBean = getReference(beanManager, YourBean.class);
// ...
Or, if you're already using OmniFaces 1.x, or are open to using it, use its Beans utility class (available since 1.6 only):
YourBean yourBean = Beans.getReference(YourBean.class);
// ...
Both return a proxy reference, you could safely assign it as an instance variable of the SystemEventListener class during its construction.

Related

Retrieve a list of all GET params?

Is there an easy way to quickly get a list of all the GET parameters in the URL using CDI injection?
What I am looking for is something like:
#Inject
Map<String, String> allGetParams;
Like it has been said, you don't have it out of the box, but you can produce it like that in CDI 1.1+
public class MyProducer {
#Produces
#RequestScoped
Map<String,String[]> produceParamMap(HttpServletRequest request) {
return request.getParameterMap();
}
}
in CDI 1.0 you'll have to use Apache Deltaspike servlet module and write nearly the same
public class MyProducer {
#Produces
#RequestScoped
Map<String,String[]> produceParamMap(#Deltaspike HttpServletRequest request) {
return request.getParameterMap();
}
}
EDIT : more details
A producer (declared with #Produces annotation) is a way to declare a bean in CDI. Such a bean can be injected like any others. Here it'll be:
#Inject
Map<String,String[]> params;
So you don't call a producer method, it's the CDI container that uses it to instantiate a bean.
When you declare a producer method with parameters, the container will look for beans matching these parameter in order to call the method. Existence of these beans is check at boot time and they don't exist an exception is thrown (like any injection point with unsatisfied bean).
Here, in both case the bean of type HttpServletRequest is provided for you by CDI implementation (CDI 1.1+) or Deltaspike framework (CDI 1.0).

#Inject-ed value null in #FacesComponent

I have the impression that CDI is not working with classes that have a #javax.faces.component.FacesComponent. Is this true?
Here's my example, that doesn't work. The MyInjectableClass is used at other points in the code where injection is not a problem, so it must be about the #FacesComponent annotation I think.
The class I want to inject:
#Named
#Stateful
public class MyInjectableClass implements Serializable {
private static final long serialVersionUID = 4556482219775071397L;
}
The component which uses that class;
#FacesComponent(value = "mycomponents.mytag")
public class MyComponent extends UIComponentBase implements Serializable {
private static final long serialVersionUID = -5656806814384095309L;
#Inject
protected MyInjectableClass injectedInstance;
#Override
public void encodeBegin(FacesContext context) throws IOException {
/* injectedInstance is null here */
}
}
Unfortunately, even for JSF 2.2 #FacesComponent, #FacesValidator and #FacesConverter are not valid injection targets (read What's new in JSF 2.2? by Arjan Tijms for more details). As Arjan points out:
It’s likely that those will be taken into consideration for JSF 2.3 though.
What can you do for now? Well, you've got basically two choices:
Handle CDI injection via lookup, or switch to EJB and do the simpler EJB lookup;
Annotate tour class with #Named instead of #FacesComponent, #Inject the component the way you did and register your component in faces-config.xml. As the UI component instance is created via JSF Application#createComponent(), not via CDI you will also need a custom Application implementation as well (exactly like OmniFaces has for those converters/validators).
And, by the way, you've got two issues with what you've got this far: (1) what is meant by #Named #Stateful when the former is from a CDI world and the latter is from EJB world and (2) are you sure you intend to keep state in a faces component that's basically recreated on every request?
#FacesCompnent is managed by JSF and injection is not supported into them.
Passing the value in from the XHTML page via a composite component attribute worked for us.

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.

Correct way of retrieving another bean instance from context [duplicate]

This question already has answers here:
Get JSF managed bean by name in any Servlet related class
(6 answers)
Closed 2 years ago.
We are using the following code to get the managed bean instance from the context.
FacesUtils.getManagedBean("beanName");
Is it the correct way of doing it?. If multiple users access the same bean what will happen?
How the bean instances are managed?
Since FacesUtils is not part of standard JSF implementation, it's unclear what it is actually doing under the covers.
Regardless, when you're already inside a managed bean, then the preferred way is to inject the other bean as managed property. I'll assume that you're already on JSF 2.0, so here's a JSF 2.0 targeted example.
#ManagedBean
#SessionScoped
public void OtherBean {}
#ManagedBean
#RequestScoped
public void YourBean {
#ManagedProperty("#{otherBean}")
private void OtherBean;
#PostConstruct
public void init() {
otherBean.doSomething(); // OtherBean is now available in any method.
}
public void setOtherBean(OtherBean otherBean) {
this.otherBean = otherBean;
}
// Getter is not necessary.
}
But when you're still on JSF 1.x, then you need to do it by <managed-property> entry in faces-config.xml as explained in this question: Passing data between managed beans.
If you happen to use CDI #Named instead of JSF #ManagedBean, use #Inject instead of #ManagedProperty. For this, a setter method is not required.
See also:
Communication in JSF2
Get JSF managed bean by name in any Servlet related class
Backing beans (#ManagedBean) or CDI Beans (#Named)?
As to your concern
If multiple users access the same bean what will happen? How the bean instances are managed?
They are managed by JSF. If a bean is found, then JSF will just return exactly this bean. If no bean is found, then JSF will just auto-create one and put in the associated scope. JSF won't unnecessarily create multiple beans.

Inject a EJB into a JSF converter with JEE6 [duplicate]

This question already has answers here:
How to inject #EJB, #PersistenceContext, #Inject, #Autowired, etc in #FacesConverter?
(5 answers)
Closed 6 years ago.
I have a stateless EJB that acceses my database.
I need this bean in a JSF 2 converter to retreive an entity object from the String value parameter. I'm using JEE6 with Glassfish V3.
#EJB annotation does not work and gets a NPE, because it's in the faces context and it has not access to the EJB context.
My question is:
Is it still possible to Inject this bean with a #Resource or other annotation, or a JNDI lookup, or do I need a workaround?
Solution
Do a JNDI lookup like this:
try {
ic = new InitialContext();
myejb= (MyEJB) ic
.lookup("java:global/xxxx/MyEJB");
} catch (NamingException e) {
e.printStackTrace();
}
I never used JSF 2.0 (only 1.0), but chapter 5.4 of the spec says:
[...] allow the container to inject references to
container managed resources into a managed bean instance before it is made accessible to the JSF application.
Only beans declared to be in request,
session, or application scope are
eligble for resource injection.
But so far I understand, a JNDI lookup should do the trick.
Other (yet not so pretty) solution may be using binding instead of converterId. Using JSF managed beans only:
<f:converter binding="#{app.personConverter}" />
Where appBean stands for something like:
#ManagedBean(name="app")
#ApplicationScoped
class AppBean {
#EJB
private PersonService ps;
private Converter personConverter;
}
There MAY be a nicer solution in CDI-style (JSR-299) but i've failed to make this one running:
<f:converter binding="#{cdiBean}" />
Where cidBean ought to be:
#Named class CdiBean implements Converter { #EJB ... }
Fails with 'Default behavior invoked of requiring a converter-id passed in the constructor'
Anyhow first approach using binding and app scoped JSF bean works.
The Seam Faces extension for JSF 2.0 and CDI allows #Inject support directly in Validators and Converters.
Check it out: http://ocpsoft.com/java/seam-faces-3-0-0-alpha2-jsf-2-0-just-got-even-easier/
i don't know if this solution is pretty... but it does work:
#ManagedBean
public class AcquisitionConverter implements Converter
{
#EJB
private AcquisitionService service;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
...
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value)
{
...
}
}
and
<h:inputText value="#{flowController.acquisition}" converter="#{acquisitionConverter}">
with jsf 2.1.3 (mojarra) and glassfish 3.1.1

Resources