Stateful EJB does not keep property value - jsf

I am using a stateful EJB for keeping my login information:
#Stateful
public class SecurityService {
private static final Logger log4jLogger = Logger.getLogger(SecurityService.class);
#Inject UtenteDao utenteDao;
#Inject AutorizzazioneDao autorizzazioneDao;
private Utente utenteCorrente;
private Negozio negozioCorrente;
public SecurityService() {
}
public boolean authenticate() {
boolean result = false;
Principal principal = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
if (principal!=null) {
utenteCorrente = utenteDao.findByUsername(principal.getName());
}
if (negozioCorrente!=null && utenteCorrente!=null) {
Autorizzazione a = autorizzazioneDao.cercaPerNegozioAndUtente(negozioCorrente, utenteCorrente);
result = a!=null;
}
return result;
}
// ...
}
My JSF login page is controlled by:
#Named
#RequestScoped
public class LoginController {
#Inject private SecurityService securityService;
private String username;
private String password;
private Negozio negozio;
public void login() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context.getExternalContext();
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
try {
if (request.getUserPrincipal() != null) {
request.logout();
}
request.login(username, password);
securityService.setNegozioCorrente(negozio);
if (!securityService.authenticate()) {
throw new ServletException("Utente non abilitato.");
}
externalContext.redirect("/pippo/");
} catch (ServletException e) {
e.printStackTrace();
context.addMessage(null, new FacesMessage("Accesso Negato"));
}
}
public void logout() throws IOException {
//...
}
public String getLoggedUsername() {
Utente utenteCorrente = securityService.getUtenteCorrente();
String fullName = "";
if (utenteCorrente!=null) {
fullName = utenteCorrente.getNomeCompleto();
} else {
System.out.println("Utente NULLO");
}
return fullName;
}
//...
}
My users actually can login the way I want (programmatic security with some adds from my domain).
The problem I have is in the next page, when you're already logged in. I want to display in all pages header "Welcome! You're logged in as #{loginController.loggedUsername}.
I keep getting a null securityService.getUtenteCorrente().
SecurityService EJB behaves like a Stateless session bean! I want to know whether I am misunderstanding something about the Stateful EJBs, or I just omitted something for this to work as I expect.
My goal is just to have a "session-wide" bean for keeping user state. Is a EJB necessary or can I just use a SessionScoped JSF ManagedBean?

LoginController is request-scoped and your SecurityService is dependent-scoped (for all purposes it is not session-scoped unless you specify it as such). Therefore, when the second JSF page references the LoginController in a EL expression, a new instance of LoginController would be created that would have a reference to a different instance of the SecurityService SFSB.
If you need to access the original SecurityService instance, you should mark it as #SessionScoped so that clients like the LoginController can access them across requests. But then, you might also want to consider why you need a #Stateful annotation in the first place, since this task could be done by an #SessionScoped managed bean. You don't really need a SFSB to store a reference to your User/Principal objects.

In order to be managed session beans must be declared using the #EJB annotation or looked up using the JNDI, the way you inject it just gives you a plain object not managed by the app server, in fact you create a new object any time you use it.

Related

Access Session Bean Property/Inject Session Bean

Still learning JSF and Java and having trouble understanding how to access a session bean property.
I have a LoggedUser session bean which sets the user that is logged in(using the login method).
#ManagedBean(name="loggedUser")
#Stateless
#LocalBean
#SessionScoped
public class LoggedUser {
#EJB
UserEJB userEJB;
#PersistenceContext
private EntityManager em;
private UserEntity loggedUser;
private String loginUserName;
private String loginPassword;
public LoggedUser() {}
public UserEntity getLoggedUser() {
return loggedUser;
}
public void setLoggedUser(UserEntity loggedUser) {
this.loggedUser = loggedUser;
}
public String authenticate() {
if (loggedUser == null) {
return "login.xhtml";
} else {
return "";
}
}
public String login() {
if (userEJB.validateLogin(loginUserName, loginPassword)) {
setLoggedUser(userEJB.fetchUser(loginUserName));
return "index.xhtml";
}
return "";
}
public String getLoginUserName() {
return loginUserName;
}
public void setLoginUserName(String loginUserName) {
this.loginUserName = loginUserName;
}
public String getLoginPassword() {
return loginPassword;
}
public void setLoginPassword(String loginPassword) {
this.loginPassword = loginPassword;
}
}
I want to be able to view the logged user from other areas in the application. I think I am injecting it incorrectly because loggedUser is always null when I am in a different bean for example something like..
#Stateless
#LocalBean
public class HistoryEJB {
#PersistenceContext
EntityManager em;
#ManagedProperty(value = "#{loggedUser}")
private LoggedUser loggedUser;
public LoggedUser getLoggedUser() {
return loggedUser;
}
public void setLoggedUser(LoggedUser loggedUser) {
this.loggedUser = loggedUser;
}
public void testLoggedUser() {
loggedUser.getLoggedUser();
// Just an example but would be null here - why?
}
}
How can I access this property from other areas in my application? Thanks for any help.
You can't use #ManagedProperty in an EJB and you shouldn't inject a view component into a business-tier component, period. #ManagedProperty is strictly web-tier stuff and is able to inject only and into web-tier, JSF components.
Your EJB ought to have a method that accepts a LoggedUser. This way, you can then pass your logged-in user to the EJB (which is the proper flow of data in a web application). What you have now is just turning best practice on its head.
So
Add a provideLoggedUser(LoggedUser loggedUser) method to your EJB
Call that method on your instance of UserEJB from within your managed bean
Rule of Thumb: Your EJB should not be aware of the web application
It seems you are missing the setter and getter for loggedUser. In principe it is there but it is convention to name it as follows
setProperty
and
setProperty
for a field named property. Note the capital first letter of the field name in the setter and getter!

Accessing ManagedBean method from EJB

I have an EJB that handles creation of customer user accounts that needs access to a managed bean (account scoped) which manages user verification keys for user accounts (the keys are transient, so they don't need to be handled by database calls). However, I can't figure out a way to send the verification key to the EJB (which generates the verification email that is send to a user).
AccountVerifierBean.java
#ManagedBean(name = "accountVerifierBean", eager = true)
#ApplicationScoped
public class AccountVerifierBean implements Serializable {
private HashMap<String, String> verificationKeyMapping;
public AccountVerifierBean() {}
public boolean verifyKey(String username, String key) {
return key.equals(verificationKeyMapping.get(username));
}
public String generateKey(String username) {
Date time = new Date();
String key = username + time.toString();
key = Encryption.hashSHA(key);
verificationKeyMapping.put(username, key);
return key;
}
}
CustomerService.java
#Named
#Stateless
#LocalBean
public class CustomerService {
#PersistenceContext(unitName = "MovieProject-ejbPU")
private EntityManager em;
private String username;
private String password;
//getters and setters
public void validateEmail() {
Properties serverConfig = new Properties();
serverConfig.put("mail.smtp.host", "localhost");
serverConfig.put("mail.smtp.auth", "true");
serverConfig.put("mail.smtp.port", "25");
try {
Session session = Session.getInstance(serverConfig, new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("<ACCOUNT>","<PASSWORD>");
}
});
MimeMessage message = new MimeMessage( session );
message.setFrom(new InternetAddress("accounts#minimalcomputers.com","VideoPile"));
message.addRecipient(
Message.RecipientType.TO, new InternetAddress(username)
);
message.setSubject("Welcome to VideoPile!");
message.setContent("<p>Welcome to VideoPile, Please verify your email.</p><p>" + verifierKey + "</p>", "text/html; charset=utf-8"); //verifierKey is what I'm trying to get from AccountVerifierBean.
Transport.send( message );
}
catch (MessagingException ex){
Logger.getLogger(CustomerService.class.getName()).log(Level.SEVERE, null, ex);
}
catch (Exception e) {
Logger.getLogger(CustomerService.class.getName()).log(Level.SEVERE, null, e);
}
}
public String encrypt(String password) {
try {
return new String(Base64.encode(MessageDigest.getInstance("SHA").digest(password.getBytes())));
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(CustomerService.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
}
}
I've tried #Inject, #ManagedProperty, using the Application map, and using the ELContext. Nothing seems to work.
EDIT:
I think there is something wrong with the bean. Any methods called from bean don't seem to do anything (EL is resolved, no bean method calls though).
I've tested the annotations that it uses (both are javax.faces.bean.*)
So, the issue was in the AccountVerifierBean.
I added the following lines to faces-config.xml and it is now working.
<managed-bean eager="true">
<managed-bean-name>accountVerifierBean</managed-bean-name>
<managed-bean-class>org.Videotheque.beans.AccountVerifierBean</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
I'm fairly certain that the problem was because my bean needed to be in the EJB package instead of the WAR so that the EJBs can access it, but because of that, the WAR didn't know that the bean existed.

How to access property of one managed bean in another managed bean

I have a managed bean (SessionScope as follow)
#ManagedBean(name="login")
#SessionScoped
public class Login implements Serializable {
private String userSession;
public Login(){
}
}
In this managedbean, somewhere in the login function, i store the email as a session.
I have another managed bean called ChangePassword (ViewScoped). I need to access the value of the email which is stored in the userSession.
The reason of doing so is that i need to find out the current userSession(email) before i can complete the change password function. (Need change password for that specific email)
How do i do so? New to JSF, appreciate any help!
Just inject the one bean as a managed property of the other bean.
#ManagedBean
#ViewScoped
public class ChangePassword {
#ManagedProperty("#{login}")
private Login login; // +setter (no getter!)
public void submit() {
// ... (the login bean is available here)
}
// ...
}
See also:
Communication in JSF 2.0 - Injecting managed beans in each other
In JSF2, I usually use a method like this:
public static Object getSessionObject(String objName) {
FacesContext ctx = FacesContext.getCurrentInstance();
ExternalContext extCtx = ctx.getExternalContext();
Map<String, Object> sessionMap = extCtx.getSessionMap();
return sessionMap.get(objName);
}
The input parameter is the name of your bean.
if your session scoped bean is like this :
#ManagedBean(name="login")
#SessionScoped
public class Login implements Serializable {
private String userSession;
public Login(){
}
}
you can access the values of this bean like :
#ManagedBean(name="changePassword")
#ViewScoped
public class ChangePassword implements Serializable {
#ManagedProperty(value="#{login.userSession}")
private String userSession;
public ChangePassword (){
}
}
public static Object getSessionObj(String id) {
return FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(id);
}
public static void setSessionObj(String id,Object obj){
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(id, obj);
}
Add them in your managed bean :

request scoped property in session scoped JSF bean

I would like to have a session scoped JSF bean with one property that is request (page) scoped. Is it possible at all?
No, that's not possible. Managed property injection only happens during creation of the bean. However, when a session scoped bean is been created there is not necessarily a request present and the injected request scoped bean would be invalid in subsequent requests in the remnant of the session.
Do it the other way round. E.g.
#ManagedBean
#SessionScoped
public class UserManager {
private User current;
// ...
}
and
#ManagedBean
#RequestScoped
public class Login {
private String username;
private String password;
#ManagedProperty(value="#{userManager}")
private UserManager userManager;
#EJB
private UserService userService;
public String submit() {
User user = userService.find(username, password);
if (user != null) {
userManager.setCurrent(user);
return "home?faces-redirect=true";
} else {
addErrorMessage("Unknown login, please try again");
return null;
}
}
// ...
}

JSF - Get the SessionScoped Bean instance

I have this configuration on my web application. 2 beans :
1° Bean - It checks the login;
#ManagedBean(name="login")
#SessionScoped
public class Login {
private String nickname;
private String password;
private boolean isLogged;
public String getNickname() { return nickname; }
public void setNickname(String newValue) { nickname=newValue; }
public String getPassword() { return password; }
public void setPassword(String newValue) { password=newValue; }
public void checkLogin() {
... i check on db the nickname and the password ...
if(USER EXIST) {
isLogged=true;
} else {
isLogged=false;
}
return true;
}
}
2° Bean - Manage User parameter :
#ManagedBean(name="user")
#SessionScoped
public class User {
private String name;
private String surname;
private String mail;
public User() {
String[] record=null;
Database mydb=Configuration.getDatabase();
mydb.connetti();
ArrayList<String[]> db_result=null;
db_result=mydb.selectQuery("SELECT name, surname, mail, domicilio FROM users WHERE nickname='???????'");
int i = 0;
while (i<db_result.size() ) {
record=(String[]) db_result.get(i);
i++;
}
}
... getter and setter methods...
}
As you can see, I would like to know how get the nickname setted previously on my login bean, so i can do the query on my DB.
In fact i need to get the instance of the current-session bean login : how can I get it? I should use somethings like session.getBean("login") :)
Hope this question is clear :)
Use #ManagedProperty to inject it and use #PostConstruct to access it after bean's construction (because in a normal constructor it would be still null).
#ManagedBean
#SessionScoped
public class User {
#ManagedProperty(value="#{login}")
private Login login;
#PostConstruct
public void init() {
// Put original constructor code here.
}
// Add/generate getters/setters and other boilerplate.
}
That said, this is not the correct approach. You'd like to do it the other way round. Inject User in Login by #ManagedProperty(value="#{user}") and do the job during submit action method.
You'd also like to put the password in WHERE clause as well. There's absolutely no need to haul the entire users table into Java's memory and determine it one by one. Just let the DB do the job and check if it returns zero or one row.
Also try using the following code:
ExternalContext tmpEC;
Map sMap;
tmpEC = FacesContext.getCurrentInstance().getExternalContext();
sMap = tmpEC.getSessionMap();
login loginBean = (login) sMap.get("login");

Resources