I have two managed Java beans:
import javax.annotation.PostConstruct;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.ws.rs.Path;
#Path("/sync")
#ManagedBean(name="syncService", eager=true)
#ApplicationScoped
public class SyncService {
#ManagedProperty(value="#{ldapDirectoryAccess}")
private DirectoryAccess directoryAccess;
public void setDirectoryAccess(DirectoryAccess directoryAccess) {
System.out.println("SyncService.setDirectoryAccess()");
this.directoryAccess = directoryAccess;
}
public SyncService() {
System.out.println("SyncService() - constructed: " + this);
}
#PostConstruct
public void init() {
System.out.println("DirectoryAccess injected: " + directoryAccess + " in: " + this);
}
...
}
#ManagedBean(name="ldapDirectoryAccess", eager=true)
#ApplicationScoped
public class LdapDirectoryAccess implements DirectoryAccess {
public LdapDirectoryAccess() {
System.out.println("LdapDirectoryAccess constructed: " + this);
}
...
}
When I deploy the application in Tomcat, I get the following output in catalina.out:
SyncService() - constructed: ...SyncService#705ebb4d
...
LdapDirectoryAccess constructed: ...LdapDirectoryAccess#3c1fd5aa
SyncService.setDirectoryAccess()
DirectoryAccess injected: ...LdapDirectoryAccess#3c1fd5aa in:
...SyncService#705ebb4d
LdapDirectoryAccess constructed: ...LdapDirectoryAccess#59d6a4d1
So, first an instance of each bean is constructed as expected, and the second bean is injected into the first. But then, another instance of the second bean class is created. How is this possible? In this tutorial I found the following:
#ApplicationScoped
Bean lives as long as the web application lives. It gets created upon
the first HTTP request involving this bean in the application (or when
the web application starts up and the eager=true attribute is set in
#ManagedBean) and gets destroyed when the web application shuts down.
So I would expected that one instance of each bean is created when the application is started, and that both instances are destroyed when the application is shut down. But LdapDirectoryAccess is constructed twice.
Moreover, when I open the page that is served by SyncService I see:
SyncService() - constructed: ... SyncService#1cb4a09c
so a second instance of SyncService is built as well, and I cannot understand why. Also, no directoryAccess property is injected this time, and the service throws a null pointer exception.
This means that the first instance of SyncService is built correctly, but then
A second instance of SyncService is created (why?)
No LdapDirectoryAccess is injected into it (why?)
This second instance of SyncService is used to serve the call to my REST API. Why this instance and not the first one that was created?
I have looked at this question and its answers, however:
I am using Mojarra 2.2.18
My application's web.xml does not contain any tag mentioning com.sun.faces.config.ConfigureListener
So I after several hours investigation I am completely out of ideas. Do you have any hints?
A second instance of SyncService is created (why?)
Because two different frameworks, completely unaware of each other, are being instructed to manage (instantiate and use) it.
JAX-RS, via #Path
JSF, via #ManagedBean
So, in effects, you have one instance of SyncService which is managed by JAX-RS, and you have another instance of SyncService which is managed by JSF, and only in this instance, the also JSF-specific #ManagedProperty is recognized. JAX-RS doesn't understand the #ManagedProperty and thus does nothing with it.
Basically, you're here tight-coupling a JAX-RS resource and a JSF managed bean in the very same class. Tight-coupling is a bad programming practice. You need to split out SyncService into one independent JAX-RS resource and one independent JSF managed bean. And you'd need to convert the LdapDirectoryAccess to use another bean management framework which is recognized by both JAX-RS and JSF, so that a single instance can be injected in both. In modern Java EE 8, that would be a bean managed by CDI's #javax.enterprise.context.ApplicationScoped. In legacy Java EE 6/7, you could abuse EJB's #javax.ejb.Singleton for that.
Given that you're still using the deprecated #ManagedBean instead of its replacement #Named, I'll assume that you're still on legacy Java EE, so I'll show only the EJB approach.
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
#Singleton
public class LdapDirectoryAccessService implements DirectoryAccess {
#PostConstruct
public void init() {
System.out.println("LdapDirectoryAccess constructed: " + this);
}
}
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ws.rs.Path;
#Path("/sync")
public class SyncResource {
#EJB
private DirectoryAccess directoryAccess;
#PostConstruct
public void init() {
System.out.println("DirectoryAccess injected: " + directoryAccess + " in: " + this);
}
}
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.RequestScoped;
import javax.faces.bean.ManagedBean;
#ManagedBean
#RequestScoped
public class SyncBacking {
#EJB
private DirectoryAccess directoryAccess;
#PostConstruct
public void init() {
System.out.println("DirectoryAccess injected: " + directoryAccess + " in: " + this);
}
}
Note that being able to inject an EJB in a JAX-RS resource might require additional configuration in Java EE 6/7, for that see the first link of the list below. And, in case you want to LdapDirectoryAccessService to eagerly initialize during server's startup, add the #javax.ejb.Startup annotation.
See also:
Inject an EJB into JAX-RS (RESTful service)
JSF Controller, Service and DAO
Backing beans (#ManagedBean) or CDI Beans (#Named)?
How to choose the right bean scope?
Related
I'm beggining with Hibernate Envers. I'm already able to properly annotate classes with #Audited and create revisions, but I'm unable to record logged user data with my revisions.
My JSF 2.0 test application is running on CDI, JPA/Hibernate in a jbossEAP6 / wildfly server. I'm neither using Spring or Seam.
Here is some code:
revisionEntity.java
#Entity
#RevisionEntity(AuditListener.class)
public class RevisionEntity {
#Id
#GeneratedValue
#RevisionNumber
private int id;
#RevisionTimestamp
private long timestamp;
private String username;
LoginBean.java
#Named
#Stateful
#SessionScoped
public class LoginBean implements Serializable{
private String username;
...
AuditListener.java
import javax.ejb.Stateful;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.hibernate.envers.RevisionListener;
import br.test.login.filterlogin.beans.LoginBean;
#Named
#Stateful
#SessionScoped
public class AuditListener implements RevisionListener {
#Inject
private LoginBean loginBean;
public void newRevision(Object revisionEntity) {
RevisionEntityEx RevEntity = (RevisionEntityEx) revisionEntity;
RevEntity.setUsername(loginBean.getUsername());
}
The loginBean injection fails, giving me a NullPointerException. Any ideas?
Sorry about my terrible grammar.
Regards,
Marcelo.
The listener is not managed by the container so your loginBean will not be injected.
We need to lookup for it...
Notice that UsuarioService should be changed to your type: LoginBean.
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.CDI;
BeanManager beanManager = CDI.current().getBeanManager();
Bean<UsuarioService> bean = (Bean<UsuarioService>) beanManager.getBeans(UsuarioService.class).iterator().next();
CreationalContext<UsuarioService> context = beanManager.createCreationalContext(bean);
this.usuarioService = (UsuarioService) beanManager.getReference(bean, UsuarioService.class, context);
You didn't give any stacktrace, so I'm guessing. AFAIK you cannot combine together both
#Stateful
#SessionScoped
Because the first annotation is for making a class an EJB stateful session bean, but the second one is for making class a CDI managed bean, with scope Session.
It seems to me that you are trying to use technologies you don't understand at all. Please read a specification or at least some CDI tutorials/ sample GitHub projects.
Personal advice: most of the time you should prefer to use #Stateless over #Stateful for EJB beans. Then all data related to HTTP session you can store e.g. in some additional #SessionScoped CDI bean.
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 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 use CDI to annotate beans. One bean called SessionManager holds the logined user information with the declaration:
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import javax.ejb.Stateful;
#Stateful
#Named
#SessionScoped
public class SessionManagerImpl implements SessionManager, Serializable {
...
public UserDto getLoginedUser() {
...
}
}
And the other is called DashboardController as:
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
import javax.inject.Inject;
#Named
#RequestScoped
public class DashboardController implements Serializable {
#Inject
private SessionManager sessionManager;
...
public void loadUserInfo() {
...
UserDto userDto = sessionManager.getLoginedUser();
}
}
The first time i open a page refer DashboardController, it works well. And if i continue to use the website, it still works. But if i don't click any page for some minutes, and come back to open the page, it will display a null pointer for the javassist$$getLoginedUser method invocation (sessionManager is not null when i use debug to watch). The session is still valid for i can get values from session map directly using faces context.
What's wrong with the SessionManager? Thanks.
This occurs because your Stateful Session Bean (EJB) has passivated, and is not reintroduced to your session. If there isn't a strong need to make your session scoped object a session bean, I would just make it a SessionScoped managed bean.
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.