Cannot inject CDI #SessionScoped in HttpSessionListener - jsf

I have a stateful, session scoped (CDI) EJB that holds the info about the user session.
#Stateful
#SessionScoped
public class GestorSesion implements IGestorSesionLocal, Serializable {
private final static long serialVersionUID = 1L;
private static final Logger log = Logger.getLogger(GestorSesion.class.getName());
#PostConstruct
private void init() {
log.info("Configurando información de usuario");
log.info("****************************************************************************");
}
#Override
public void cerrarSesion() {
}
#Override
public ISessionInfo getSesionInfo() {
return null;
}
}
Now, I want to call cerrarSesion() (closeSession()) from an HttpSessionListener
public class GestorSesionWeb implements HttpSessionListener {
private static final Logger log = Logger.getLogger(GestorSesionWeb.class.getName());
#Inject
private Instance<IGestorSesionLocal> gestorSesion;
#Override
public void sessionCreated(HttpSessionEvent se) {
if (log.isLoggable(Level.FINE)) {
log.fine("Iniciada sesión web");
}
gestorSesion.get().getSesionInfo();
}
#Override
public void sessionDestroyed(HttpSessionEvent se) {
if (log.isLoggable(Level.FINE)) {
log.fine("Limpiando sesión al salir");
}
try {
this.gestorSesion.get().cerrarSesion();
if (log.isLoggable(Level.FINE)) {
log.fine("Sesión limpiada sin problemas");
}
} catch (Exception e) {
log.log(Level.WARNING, "Excepción limpiando sesión", e);
}
}
}
And I access the EJB from the webapp, directly (injecting using #EJB) into the beans that I use for JSF (they are also CDI managed beans).
The issue that I face is that it seems that the HttpSessionListener seems to be in a different "session scope" than the JSF beans. Two instances of GestorSession are created; one instantiated from the JSF and other instantiated from the HttpSessionListener.
I have tried injecting the bean via #Inject Instance<IGestorSesionLocal>, #Inject IGestorSesionLocal and BeanManager, with identical results.
This bug report suggests that it should to work correctly (the bug is solved for my version), but I still cannot get around it. I have looked around, but what I find are Q&A relating to JSF managed beans (yes, I could try to wrap a reference to the EJB in a JSF managed bean, but I would like to try it "correctly" first).
Working with WilFly 8.1.0 and JDK 7
Any ideas?

I think your pb comes from here:
And I access the EJB from the webapp, directly (injecting using
#EJB) into the JSF beans.
When use CDI with EJB (by putting a #Sessionscoped on it for instance) you got an CDI bean that also has an EJB nature but not the other way around. In other word CDI is aware of the EJB nature but EJB is not aware of CDI.
So if you want to inject an EJB as a CDI bean in your code use #Inject and not #EJB. From Java EE 6 the admitted good practice is to always use #Inject except for EJB remote.
Also be sure to only use CDI managed bean and not a mix of CDI and JSF managed bean.

Related

JSF Converter - NullPointerException [duplicate]

How can I inject a dependency like #EJB, #PersistenceContext, #Inject, #AutoWired, etc in a #FacesConverter? In my specific case I need to inject an EJB via #EJB:
#FacesConverter
public class MyConverter implements Converter {
#EJB
protected MyService myService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
// myService.doSomething
}
}
However, it didn't get injected and it remains null, resulting in NPEs. It seems that #PersistenceContext and #Inject also doesn't work.
How do I inject a service dependency in my converter so that I can access the DB?
Can I use #EJB to inject my service into a #FacesConverter?
No, not until JSF 2.3 is released. The JSF/CDI guys are working on that for JSF 2.3. See also JSF spec issue 1349 and this related "What's new in JSF 2.3?" article of my fellow Arjan Tijms. Only then dependency injection like #EJB, #PersistenceContext, #Inject, etc will work in a #FacesConverter when you explicitly add managed=true attribute to the annotation.
#FacesConverter(value="yourConverter", managed=true)
public class YourConverter implements Converter {
#Inject
private YourService service;
// ...
}
If not, what's the "correct" way to do this?
Before JSF 2.3, you have several options:
Make it a managed bean instead. You can make it a JSF, CDI or Spring managed bean via #ManagedBean, #Named or #Component. The below example makes it a JSF managed bean.
#ManagedBean
#RequestScoped
public class YourConverter implements Converter {
#EJB
private YourService service;
// ...
}
And the below example makes it a CDI managed bean.
#Named
#RequestScoped
public class YourConverter implements Converter {
#Inject
private YourService service;
// ...
}
Reference it as <h:inputXxx converter="#{yourConverter}"> instead of <h:inputXxx converter="yourConverter">, or as <f:converter binding="#{yourConverter}"> instead of <f:converter converterId="yourConverter">. Don't forget to remove the #FacesConverter annotation!
The disadvantage is that you cannot specify forClass and thus need to manually define the converter everywhere in the view where necessary.
Inject it in a regular managed bean instead.
#ManagedBean
#RequestScoped
public class YourBean {
#EJB
private YourService service;
// ...
}
And in your converter, grab or call it via EL.
YourBean yourBean = context.getApplication().evaluateExpressionGet(context, "#{yourBean}", YourBean.class);
// Then e.g. either
YourEntity yourEntity = yourBean.getService().findByStringId(value);
// Or
YourEntity yourEntity = yourBean.findEntityByStringId(value);
This way you can keep using #FacesConverter.
Manually grab the EJB from JNDI.
YourService yourService = (YourService) new InitialContext().lookup("java:global/appName/YourService");
The disadvantage is that there is a certain risk that this is not entirely portable. See also Inject EJB bean from JSF managed bean programmatically.
Install OmniFaces. Since version 1.6, it transparently adds support for #EJB (and #Inject) in a #FacesConverter without any further modification. See also the showcase. If you happen to need the converter for <f:selectItem(s)>, then the alternative is to use its SelectItemsConverter which will automatically perform the conversion job based on select items without the need for any database interaction.
<h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">
See also Conversion Error setting value for 'null Converter'.
See also:
How to inject in #FacesValidator with #EJB, #PersistenceContext, #Inject, #Autowired
CDI Injection into a FacesConverter
Getting an #EJB in a #FacesValidator and #FacesConverter
The answer is Yes if you can accommodate Seam Faces module in your web application. Please check this post Injection of EntityManager or CDI Bean in FacesConverter. You can use #EJB in similar fashion.
You could access it indirectly through FacesContext, which is a parameter in both Converter methods.
The converter could be also annotated CDI Named with Application scope. When accessing the facade, two instances of the same class are used. One is the converter instance itself, dumb, without knowing EJB annotation. Another instance retains in application scope and could be accessed via the FacesContext. That instance is a Named object, thus it knows the EJB annotation. As everything is done in a single class, access could be kept protected.
See the following example:
#FacesConverter(forClass=Product.class)
#Named
#ApplicationScoped
public class ProductConverter implements Converter{
#EJB protected ProductFacade facade;
protected ProductFacade getFacadeFromConverter(FacesContext ctx){
if(facade==null){
facade = ((ProductConverter) ctx.getApplication()
.evaluateExpressionGet(ctx,"#{productConverter}",ProductConverter.class))
.facade;
}
return facade;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return getFacadeFromConverter(context).find(Long.parseLong(value));
}
...
#Inject will only works in CDI managed instances
This only works at least Java EE 7 and CDI 1.1 server:
#FacesConverter
public class MyConverter implements Converter {
protected MyService myService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
myService = CDI.current().select(MyService .class).get();
myService.doSomething();
}
}
https://docs.oracle.com/javaee/7/api/javax/enterprise/inject/spi/CDI.html
https://stackoverflow.com/a/33017416/5626568
By Luis Chacon, Sv
Works fine, tested
definition EJB :
#Stateless
#LocalBean
public class RubroEJB {
#PersistenceContext(unitName = "xxxxx")
private EntityManager em;
public List<CfgRubroPres> getAllCfgRubroPres(){
List<CfgRubroPres> rubros = null;
Query q = em.createNamedQuery("xxxxxxx");
rubros = q.getResultList();
return rubros;
}
}
define bean with the Aplication bean scope, for get the EJB Object
#ManagedBean(name="cuentaPresService", eager = true)
#ApplicationScoped
public class CuentaPresService {
#EJB
private RubroEJB cfgCuentaEJB;
public RubroEJB getCfgCuentaEJB() {
return cfgCuentaEJB;
}
public void setCfgCuentaEJB(RubroEJB cfgCuentaEJB) {
this.cfgCuentaEJB = cfgCuentaEJB;
}
}
final Access to Ejb Object from Converter:
#FacesConverter("cuentaPresConverter")
public class CuentaPresConverter implements Converter {
#EJB
RubroEJB rubroEJB;
public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
if(value != null && value.trim().length() > 0) {
try {
CuentaPresService service = (CuentaPresService) fc.getExternalContext().getApplicationMap().get("cuentaPresService");
List<CfgCuentaPres> listCuentas=service.getCfgCuentaEJB().getAllCfgCuentaPres();
................

#ManagedBean(name="foo") not available by its name in EL #{foo}

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.

Getting JSF ServletContext in non-request environment in a CDI bean

I am using TomEE+ 1.7.1.
With JSF managed beans this code was working well:
#ManagedBean( eager = true )
#ApplicationScoped
public class AppBean {
#PostConstruct
public void init() {
ServletContext sc = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext();
if (GlobalSettings.TESTMODE) {
sc.getSessionCookieConfig().setDomain("." + GlobalSettings.APP_DOMAIN_TEST);
} else {
sc.getSessionCookieConfig().setDomain("." + GlobalSettings.APP_DOMAIN);
}
}
}
The init function ran at application startup and ServletContext was available.
I read everywhere that it's time to migrate to CDI beans instead of JSF beans. So I wanted to change #ManagedBean( eager = true ) to #Named #Eager (#Eager is from Omnifaces). Init function is running at application startup, but there is no FacesContext so I can't get ServletContext.
General question: How to get ServletContext in a non-request environment in CDI beans? (ServletContext is not a 'per request' object, so it should exist before the first request.)
Specific question: how to set the domain for the session cookies dynamically from code but before the first request occurs?
You should be using a ServletContextListener for the purpose of performing programmatic configuration on a servlet based application.
#WebListener
public class Config implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();
// ...
}
#Override
public void contextDestroyed(ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();
// ...
}
}
A #WebListener is inherently also CDI managed and thus you can just use #Inject and friends in there.
An application scoped managed bean is intented for holding application scoped data/state which can be used/shared across requests/views/sessions.
Per the CDI spec, you can #Inject the ServletContext into a CDI bean. Just be sure to do it in a #PostConstruct, as injected fields are available only after construction:
#Inject ServletContext extCtxt;
#PostConstruct
public void doSomething(){
// do something with your injected field
}

How to inject #EJB, #PersistenceContext, #Inject, #Autowired, etc in #FacesConverter?

How can I inject a dependency like #EJB, #PersistenceContext, #Inject, #AutoWired, etc in a #FacesConverter? In my specific case I need to inject an EJB via #EJB:
#FacesConverter
public class MyConverter implements Converter {
#EJB
protected MyService myService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
// myService.doSomething
}
}
However, it didn't get injected and it remains null, resulting in NPEs. It seems that #PersistenceContext and #Inject also doesn't work.
How do I inject a service dependency in my converter so that I can access the DB?
Can I use #EJB to inject my service into a #FacesConverter?
No, not until JSF 2.3 is released. The JSF/CDI guys are working on that for JSF 2.3. See also JSF spec issue 1349 and this related "What's new in JSF 2.3?" article of my fellow Arjan Tijms. Only then dependency injection like #EJB, #PersistenceContext, #Inject, etc will work in a #FacesConverter when you explicitly add managed=true attribute to the annotation.
#FacesConverter(value="yourConverter", managed=true)
public class YourConverter implements Converter {
#Inject
private YourService service;
// ...
}
If not, what's the "correct" way to do this?
Before JSF 2.3, you have several options:
Make it a managed bean instead. You can make it a JSF, CDI or Spring managed bean via #ManagedBean, #Named or #Component. The below example makes it a JSF managed bean.
#ManagedBean
#RequestScoped
public class YourConverter implements Converter {
#EJB
private YourService service;
// ...
}
And the below example makes it a CDI managed bean.
#Named
#RequestScoped
public class YourConverter implements Converter {
#Inject
private YourService service;
// ...
}
Reference it as <h:inputXxx converter="#{yourConverter}"> instead of <h:inputXxx converter="yourConverter">, or as <f:converter binding="#{yourConverter}"> instead of <f:converter converterId="yourConverter">. Don't forget to remove the #FacesConverter annotation!
The disadvantage is that you cannot specify forClass and thus need to manually define the converter everywhere in the view where necessary.
Inject it in a regular managed bean instead.
#ManagedBean
#RequestScoped
public class YourBean {
#EJB
private YourService service;
// ...
}
And in your converter, grab or call it via EL.
YourBean yourBean = context.getApplication().evaluateExpressionGet(context, "#{yourBean}", YourBean.class);
// Then e.g. either
YourEntity yourEntity = yourBean.getService().findByStringId(value);
// Or
YourEntity yourEntity = yourBean.findEntityByStringId(value);
This way you can keep using #FacesConverter.
Manually grab the EJB from JNDI.
YourService yourService = (YourService) new InitialContext().lookup("java:global/appName/YourService");
The disadvantage is that there is a certain risk that this is not entirely portable. See also Inject EJB bean from JSF managed bean programmatically.
Install OmniFaces. Since version 1.6, it transparently adds support for #EJB (and #Inject) in a #FacesConverter without any further modification. See also the showcase. If you happen to need the converter for <f:selectItem(s)>, then the alternative is to use its SelectItemsConverter which will automatically perform the conversion job based on select items without the need for any database interaction.
<h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">
See also Conversion Error setting value for 'null Converter'.
See also:
How to inject in #FacesValidator with #EJB, #PersistenceContext, #Inject, #Autowired
CDI Injection into a FacesConverter
Getting an #EJB in a #FacesValidator and #FacesConverter
The answer is Yes if you can accommodate Seam Faces module in your web application. Please check this post Injection of EntityManager or CDI Bean in FacesConverter. You can use #EJB in similar fashion.
You could access it indirectly through FacesContext, which is a parameter in both Converter methods.
The converter could be also annotated CDI Named with Application scope. When accessing the facade, two instances of the same class are used. One is the converter instance itself, dumb, without knowing EJB annotation. Another instance retains in application scope and could be accessed via the FacesContext. That instance is a Named object, thus it knows the EJB annotation. As everything is done in a single class, access could be kept protected.
See the following example:
#FacesConverter(forClass=Product.class)
#Named
#ApplicationScoped
public class ProductConverter implements Converter{
#EJB protected ProductFacade facade;
protected ProductFacade getFacadeFromConverter(FacesContext ctx){
if(facade==null){
facade = ((ProductConverter) ctx.getApplication()
.evaluateExpressionGet(ctx,"#{productConverter}",ProductConverter.class))
.facade;
}
return facade;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return getFacadeFromConverter(context).find(Long.parseLong(value));
}
...
#Inject will only works in CDI managed instances
This only works at least Java EE 7 and CDI 1.1 server:
#FacesConverter
public class MyConverter implements Converter {
protected MyService myService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
myService = CDI.current().select(MyService .class).get();
myService.doSomething();
}
}
https://docs.oracle.com/javaee/7/api/javax/enterprise/inject/spi/CDI.html
https://stackoverflow.com/a/33017416/5626568
By Luis Chacon, Sv
Works fine, tested
definition EJB :
#Stateless
#LocalBean
public class RubroEJB {
#PersistenceContext(unitName = "xxxxx")
private EntityManager em;
public List<CfgRubroPres> getAllCfgRubroPres(){
List<CfgRubroPres> rubros = null;
Query q = em.createNamedQuery("xxxxxxx");
rubros = q.getResultList();
return rubros;
}
}
define bean with the Aplication bean scope, for get the EJB Object
#ManagedBean(name="cuentaPresService", eager = true)
#ApplicationScoped
public class CuentaPresService {
#EJB
private RubroEJB cfgCuentaEJB;
public RubroEJB getCfgCuentaEJB() {
return cfgCuentaEJB;
}
public void setCfgCuentaEJB(RubroEJB cfgCuentaEJB) {
this.cfgCuentaEJB = cfgCuentaEJB;
}
}
final Access to Ejb Object from Converter:
#FacesConverter("cuentaPresConverter")
public class CuentaPresConverter implements Converter {
#EJB
RubroEJB rubroEJB;
public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
if(value != null && value.trim().length() > 0) {
try {
CuentaPresService service = (CuentaPresService) fc.getExternalContext().getApplicationMap().get("cuentaPresService");
List<CfgCuentaPres> listCuentas=service.getCfgCuentaEJB().getAllCfgCuentaPres();
................

Can't #Inject a #ManagedBean in another #ManagedBean

Ok here is my session bean. I can always retrieve the currentUser from any Servlet or Filter. That's not the problem The problem is the fileList, and currentFile. I've tested with simple int's and Strings and its' the same effect. If I set a value from my view scoped bean I can get the data from another class.
#ManagedBean(name = "userSessionBean")
#SessionScoped
public class UserSessionBean implements Serializable, HttpSessionBindingListener {
final Logger logger = LoggerFactory.getLogger(UserSessionBean.class);
#Inject
private User currentUser;
#EJB
UserService userService;
private List<File> fileList;
private File currentFile;
public UserSessionBean() {
fileList = new ArrayList<File>();
currentFile = new File("");
}
#PostConstruct
public void onLoad() {
Principal principal = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
String email = principal.getName();
if (email != null) {
currentUser = userService.findUserbyEmail(email);
} else {
logger.error("Couldn't find user information from login!");
}
}
Here is an example.
My view scoped bean. This is how it is decorated.
#ManagedBean
#ViewScoped
public class ViewLines implements Serializable {
#Inject
private UserSessionBean userSessionBean;
Now the code.
userSessionBean.setCurrentFile(file);
System.out.println("UserSessionBean : " + userSessionBean.getCurrentFile().getName());
I can see the current file name perfectly. This is actually being printed from a jsf action method. So obviously the currentFile is being set.
Now if I do this.
#WebFilter(value = "/Download")
public class FileFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpSession session = ((HttpServletRequest) request).getSession(false);
UserSessionBean userSessionBean = (UserSessionBean) session.getAttribute("userSessionBean");
System.out.println(userSessionBean.getCurrentUser().getUserId()); //works
System.out.println("File filter" + userSessionBean.getCurrentFile().getName()); //doesn't work
chain.doFilter(request, response);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}
currentUser shows up fine but I can't see the file. It's just blank. The same thing happens with Strings, int's, etc.
Thanks for any help you can provide on this.
INFO: UserSessionBean : Line 3B--8531268875812004316.csv (value printed from view scoped bean)
INFO: File filter tester.csv (value printed when filter is ran.)
**EDIT**
This worked.
FacesContext context = FacesContext.getCurrentInstance();
userSessionBean = (UserSessionBean) context.getApplication().evaluateExpressionGet(context, "#{userSessionBean}", UserSessionBean.class);
I put this in the constructor of the ViewScoped and everything was fine. Now why isn't the inject doing what I thought? At first I thought maybe because I was using JSF managed beans instead of the new CDI beans. But I changed the beans to the new style(with named) and that was the same effect.
Does the inject only allow you to access the beans but not change their attributes?
You're mixing JSF and CDI. Your UserSessionBean is a JSF #ManagedBean, yet you're using CDI #Inject to inject it in another bean. CDI doesn't reuse the JSF managed one, it instead creates a brand new one. Use the one or the other, not both. The correct annotation to inject a JSF-managed bean is #ManagedProperty.
Replace
#Inject
private UserSessionBean userSessionBean;
by
#ManagedProperty(value="#{userSessionBean}")
private UserSessionBean userSessionBean;
and ensure that you don't have a import javax.enterprise.context anywhere in your code (which is the package of CDI annotations).
Alternatively, migrate all JSF bean management annotations to CDI bean management annotations.
import javax.inject.Named;
import javax.enterprise.context.SessionScoped;
#Named
#SessionScoped
public class UserSessionBean implements Serializable {}
import javax.inject.Named;
import javax.faces.view.ViewScoped;
#Named
#ViewScoped
public class ViewLines implements Serializable {}
Additional advantage is that you can just #Inject it inside a regular servlet or filter without the need to manually grab it as request/session/application attribute.
Moreover, JSF bean management annotations are deprecated since JSF 2.3. See also Backing beans (#ManagedBean) or CDI Beans (#Named)?
My best GUESS as to why this is happening, is because the variable file, is being set in view scope, and then passed by reference into the session scoped bean. Maybe this is happening because when the view scope bean is destroyed, it still has a reference to that variable, but doesn't bother to see if there's any other references to it in session scope, where it should be preserved. Hence, when it's destroyed, it's removed from both view and session scope in this case.
Could you try calling setCurrentFile with an object instantiated with 'new'? That might prove or disprove this hypothesis of mine.
Otherwise, my best advice would be to crack open the debugger, and see exactly where getCurrentFile is being changed.

Resources