0

I am creating a Timesheet application using JSF and am currently working on the login portion. It's a straight forward login that takes a login ID and a password and checks if it is a valid combination (though at this point I am just working on administrator login).

My problem seems to be that JSF is not updating my loginID and password fields before calling the verify action method because when I click login, it throws a NullPointerException. Here is my verify method.

/**
 * Verifies that the loginID and password is the super user combination.
 * @return true if it is, false if it is not.
 */
public String verifyUser() {
    ResourceBundle login = getSuperUserLogin();
    String user = getLoginID();
    String checkPassword = getPassword();
    if(!login.containsKey(user) || !checkPassword.equals(login.getString(user))) {
        return "regular";
    }
    return "super";
}

and my login page

<body>
    <div class = "content" align = "center">
        <h:form>
            <h3>#{msgs.loginTitle}</h3>
            <h:panelGrid columns="2" cellpadding = "4">
                    <h:outputText value = "#{msgs.userID}" styleClass="title" />
                    <h:inputText value="#{user.loginID}" styleClass="rounded"  />
                    <h:outputText value = "#{msgs.password}" styleClass="title" />
                    <h:inputSecret value="#{user.password}" styleClass="rounded" />
             </h:panelGrid>
             <p><h:commandButton value="#{msgs.login}" styleClass="button" action="#{superUser.verifyUser}"/></p>
          </h:form>
      </div>

Now if I hardcode the login ID and password into the user and checkPassword fields, then it works no problem. So I figure that the problem be that loginID and password fields are null (as getLoginID() and getPassword() are just regular old getters that return those fields). I know the answer is going to be something silly and that I am going to feel embarrassed for missing it but I am hitting my head on a wall at this point.

Just so information is complete, the loginID and password fields are located in the a User class (with appropriate setters provided), while the verifyUser method is located in a subclass SuperUser. I don't know why if this would matter at all, but this would be my first JSF application using super and subclasses so you never know.

Edit: On request here is my User class code

package ca.bcit.infosys.timesheet.model;
import java.util.ResourceBundle;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

@Named
@ApplicationScoped
public class User implements java.io.Serializable {
/** The user's name */
private String name;
/** The user's employee number */
private int empNumber;
/** The user's login ID */
private String loginID;
/** The user's password */
private String password;
/** Is the user currently in an editable state (i.e. the user's information can be edited on the webpage) */
private boolean editable;
private ResourceBundle superUserLogin = ResourceBundle.getBundle("ca.bcit.infosys.timesheet.model.messages");

/**
 * @return the name
 */
public String getName() {
    return name;
}

/**
 * @param name the name to set
 */
public void setName(String name) {
    this.name = name;
}

/**
 * @return the empNumber
 */
public int getEmpNumber() {
    return empNumber;
}

/**
 * @param empNumber the empNumber to set
 */
public void setEmpNumber(int empNumber) {
    this.empNumber = empNumber;
}

/**
 * @return the loginID
 */
public String getLoginID() {
    return loginID;
}

/**
 * @param loginID the loginID to set
 */
public void setLoginID(String loginID) {
    this.loginID = loginID;
}

/**
 * @return the password
 */
public String getPassword() {
    return password;
}

/**
 * @param password the password to set
 */
public void setPassword(String password) {
    this.password = password;
}

/**
 * @return the isEditable
 */
public boolean isEditable() {
    return editable;
}

/**
 * @param isEditable the isEditable to set
 */
public void setEditable(boolean editable) {
    this.editable = editable;
}

/**
 * @return the Property containing the super user login information.
 */
public ResourceBundle getSuperUserLogin() {
    return superUserLogin;
}

}

user1265215
  • 33
  • 1
  • 8

1 Answers1

3

There are lot of misconceptions with your current approach. I would recommend to first learn and grasp the basic concepts of JSF to accomplish this.

In order to make this work, you need a single bean that needs to be alive only for each request to the server. This bean will have the responsibility to get the data from the JSF form and execute the validation action. Then, you just have to bind the fields and login method to your JSF form.

Java code

//Looks like you use CDI (or probably you just think this is what you need)
@Named
//The bean just needs to be alive on each request
//Since you're using CDI, make sure this annotation comes from
//javax.enterprise.context package
@RequestScoped
//Usually, the managed beans used in JSF are named with "Bean" suffix
//Since it is for learning purposes, I'll name this bean UserBean
public class UserBean {
    //fields to hold username and password
    //they both are Strings
    private String username;
    private String password;
    //getters and setters methods
    //I won't add them since it is boilerplate code for this sample
    //...

    //Method to handle your login action
    //If successful login, then returns the name of the next view
    //If unsuccessful, returns null to stay in the current view
    public String login() {
        //Sample implementation, remember to change it for A REAL ONE
        if ("admin".equals(username) &&
            "adminPass".equals(password)) {
            //Successful login
            //Make sure to have a file index.xhtml in the same folder
            //where the login JSF page resides (for sample purposes)
            return "index";
        }
        //Unsuccessful login
        //Also, it is a good idea to show a message for end user
        FacesMessage message = new FacesMessage("Invalid login.");
        FacesContext.getCurrentInstance().addMessage(null, message);
        return null;
    }
}

JSF code

<h:form>
    <h3>Login Title</h3>
    <h:panelGrid columns="2">
        <h:outputText value="User Id" />
        <h:inputText value="#{userBean.username}" />
        <h:outputText value="Password" />
        <h:inputSecret value="#{userBean.password}" />
    </h:panelGrid>
    <h:commandButton value="Login" action="#{userBean.login}" />
    <br />
    <h:messages />
</h:form>

Some things you need to know before going on:

  • JSF beans are not the same as CDI beans. This is explained better here: Backing beans (@ManagedBean) or CDI Beans (@Named)?. I recommend reading both Bozho and BalusC's answers.
  • Define the scope of your managed beans. One of the main problems JSF beginners confront is to choose the right scope for the bean. For example, you started marking the bean as @ApplicationScoped which means there's a single User bean for the entire application, which means two different end users will have the same user object (now you start worring about). You can learn more about this here: How to choose the right bean scope?
  • As you may see from link above, there's a view scope. If you're working with Java EE 7, then you will find this scope available for your CDI beans as well. If you're working with Java EE 6, then you'll find that there's no annotation for this. But don't worry, OmniFaces solves this since its version 1.6 with a custom @ViewScoped annotation from org.omnifaces.cdi package.
  • Since you're starting with JSF/CDI, I recommend using a single bean per view. When you have more experience, you may use two or more beans in the same view. Here's good example: Can I use multiple managed bean in the same xhtml page?
Community
  • 1
  • 1
Luiggi Mendoza
  • 81,685
  • 14
  • 140
  • 306