0

I have a backing bean that manages CRUD operations on a user profile record. I have a form for editing/updating a user record. When I click the 'Save' button, the form clears (why?) and the backing bean method is not called (I output a logger message to the server console when the method is invoked so that I can check this).

However, after the form has cleared, if I then press 'Save' a second time, the bean method IS invoked. This is very strange behaviour. There is no validation on the form so I don't expect complications from that.

[Update] If I put in a PrimeFaces growl component then everything works i.e. if I add:

<p:growl id="growl" showDetail="true" life="2000" />

Does anyone have an idea of why this solution should be dependent on a growl? Has my app entered the twilight zone? [End of update]

I've been over the code with a fine tooth comb but I'm obviously missing something. The strange thing is, the 'Add User' form is very similar and it works fine.

Here is the 'Update user' form:

   <!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:p="http://primefaces.org/ui">

<h:head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

    <title>Admin Edit User</title>

    <h:outputStylesheet library="css" name="style.css" />

</h:head>

<h:body>

    <div id="wrapper">
        <div id="header">
            <h2>Admin Edit User</h2>
        </div>
    </div>

    <div id="container">

        <div id="content">

            <h:messages globalOnly="true" />            

            <h:form id="mainform">

            <h:outputLabel style="color:red">Specify a password only if a password reset is required, otherwise leave blank</h:outputLabel>

            <h:panelGrid columns="3">

            <h:outputLabel>Password reset:</h:outputLabel>
            <h:inputText value="#{user.password}" id="password" />
            <h:message for="password" />

            <h:outputLabel>Username:</h:outputLabel>
            <h:inputText value="#{user.username}" id="username" />
            <h:message for="username" />

            <h:outputLabel>First name:</h:outputLabel>
            <h:inputText value="#{user.firstname}" id="firstname" />
            <h:message for="firstname" />

            <h:outputLabel>Last name:</h:outputLabel>
            <h:inputText value="#{user.lastname}" id="lastname" />
            <h:message for="lastname" />

            <h:outputLabel>Email:</h:outputLabel>
            <h:inputText value="#{user.email}" id="email" />
            <h:message for="email" />

            <h:inputHidden value="#{user.id}" />
            <h:inputHidden value="#{user.emailpublic}" />
            <h:inputHidden value="#{user.receiveemail}" />
            <h:inputHidden value="#{user.homephone}" />
            <h:inputHidden value="#{user.homephonepublic}" />
            <h:inputHidden value="#{user.mobilephone}" />
            <h:inputHidden value="#{user.mobilephonepublic}" />
            <h:inputHidden value="#{user.receivetxt}" />
            <h:inputHidden value="#{user.address1}" />
            <h:inputHidden value="#{user.address2}" />
            <h:inputHidden value="#{user.address3}" />
            <h:inputHidden value="#{user.postcode}" />
            <h:inputHidden value="#{user.addresspublic}" />
            <h:inputHidden value="#{user.altcontactname}" />
            <h:inputHidden value="#{user.altcontactphone}" />
            <h:inputHidden value="#{user.altcontactemail}" />
            <h:inputHidden value="#{user.altcontactpublic}" />

            </h:panelGrid>

            <h:commandButton value="Save" action="#{userBean.updateUser(user)}" />  
            <h:commandButton value="Cancel" outcome="AdminUsers" />

            </h:form>

        </div>
    </div>

</h:body>
</html>

Here is the backing bean:

package com.mymato.coop;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

@ManagedBean(name="userBean")
@SessionScoped
public class UserBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private List<User> users;
    private UserDAO userDAO;
    private Logger logger = Logger.getLogger(getClass().getName());

    public UserBean() throws Exception {
        users = new ArrayList<>();

        userDAO = UserDAO.getInstance();
    }

    public void loadUsers() {

        if (!FacesContext.getCurrentInstance().isPostback()) {
            // this prevents multiple loads in a view scoped bean
            //logger.info("Loading users");

            users.clear();

            try {

                // get all users from database
                users = userDAO.getUsers();

            } catch (Exception exc) {
                // send this to server logs
                logger.log(Level.SEVERE, "Error loading users", exc);

                // add error message for JSF page
                addErrorMessage(exc);
            }
        }

    }

    public List<User> getUsers() {
        //logger.info("Getting users");
        return users;
    }

    public String loadUser(int userId) {

        logger.info("Loading user " + userId + " (there are " + users.size() + " users in the list)");

        try {
            // get the user from the database
            User user = userDAO.getUser(null, userId);

            logger.info("yyy Loaded user id: " + user.getId());

            ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
            Map<String, Object> requestMap = externalContext.getRequestMap();

            requestMap.put("user", user);           
        } catch(Exception exc) {
            // send this to the server logs
            logger.log(Level.SEVERE, "Error loading user id: " + userId, exc);

            // add error message for JSF page
            addErrorMessage(exc);

            // stay on current page if a message occurs
            return null;
        }

        // If all works, then go to the edit user page
        return "AdminEditUser";
    }


    public String addUser(User user) {

        logger.info("Adding user: " + user.getFirstname() + " " + user.getLastname());

        try {
            // update user in the database
            userDAO.addUser(user);

        } catch(Exception exc) {
            // send this to the server logs
            logger.log(Level.SEVERE, "Error adding user: " + user.getFirstname() + " " + user.getLastname(), exc);

            addErrorMessage(exc);

            // return to current page
            return null;
        }

        // if all works then return to the list
        return "AdminUsers?faces-redirect=true";
    }


    public String updateUser(User user) {

        logger.info("Foobar");

        logger.info("Updating user id: " + user.getId() + " Username: " + user.getFirstname() + " " + user.getLastname());

        try {
            // update user in the database
            userDAO.updateUser(user);

        } catch(Exception exc) {
            // send this to the server logs
            logger.log(Level.SEVERE, "Error updating user id: " + user.getId(), exc);

            addErrorMessage(exc);

            // return to current page
            return null;
        }

        // if all works then return to the list
        logger.info("attempting to return to user list");
        return "AdminUsers?faces-redirect=true";
    }


    public String deleteUser(int userId) {

        logger.info("Deleting user id: " + userId);

        try {
            // update user in the database
            userDAO.deleteUser(userId);

        } catch(Exception exc) {
            // send this to the server logs
            logger.log(Level.SEVERE, "Error deleting user id: " + userId, exc);

            addErrorMessage(exc);

            // return to current page
            return null;
        }

        // if all works then return to the list
        return "AdminUsers?faces-redirect=true";
    }

    private void addErrorMessage(Exception exc) {
        FacesMessage message = new FacesMessage("Error: " + exc.getMessage());
        FacesContext.getCurrentInstance().addMessage(null, message);
    }


}
tommccann
  • 133
  • 1
  • 10
  • What does the `userDAO` do? Are there any exceptions being thrown? – Sam Orozco Jul 18 '16 at 04:18
  • No, no exceptions being thrown. I don't think the method is being called at all. It seems that after the form loads (all values load successfully) the page seems to forget all about the userBean hence the values being reset to blank when the Save button is clicked. This might be a clue that it is a scope problem. Both the userBean (my controller) and the user bean are session scoped so that shouldn't be the problem. (UserDAO takes the user object and uses the attributes to fire a sql update command to the MySQL back end. This hasn't been changed for ages and I doubt this is the problem) – tommccann Jul 18 '16 at 05:20
  • I've just run the form in Chrome Inspect and looked at the Javascript console. I get this error immediately after the form loads: Uncaught TypeError: Cannot read property 'ajax' of null (primefaces.js?ln=primefaces&v=5.3:3 ) – tommccann Jul 18 '16 at 05:21
  • This is weird... Do you really not have any PrimeFaces component in this page? Are you using an (invisible to us) template? Putting the `p:growl` in there 'forces' the page to load primefaces.js. But it the error should not be there imo if you don't use any PF component on the page. – Kukeltje Jul 18 '16 at 06:40
  • No twilightzone... Try creating an [mcve]. Remove as many inputs as possible, remove as much code from the bean as possible... – Kukeltje Jul 18 '16 at 06:57
  • and what if you remove the PF namespace declaration in the page. Does that fix it to? – Kukeltje Jul 18 '16 at 06:58
  • Thanks Kukeltje. I do have the PF namespace but am not using any PF components. I'm going to play with that and see if it gets me any closer to a solution. – tommccann Jul 18 '16 at 08:03
  • I took out the growl and the PF namespace and the original problem was still there. I had to put them both back to make it work. Very odd. Do you think something is being cached somewhere - or corrupt? I've cleaned the project, removed the app from the server, cleared the Tomcat workspace and then recompiled and deployed. That didn't do the trick either. – tommccann Jul 18 '16 at 08:14
  • The odd thing is I'm using an identical technique (except for the fieldnames) to update another record type and that works perfectly. I've checked all of the getters and setters and absolutely nothing unusual about them. setters return void, getters return the appropriate types (int, String, boolean) – tommccann Jul 18 '16 at 08:17
  • Did you possibly mix the bean management (JSF/CDI)? Try to change to CDI. `@Named("userBean") @SessionScoped` with `import javax.inject.Named;` and `import javax.enterprise.context.SessionScoped;` – Holger Jul 18 '16 at 11:46
  • The symptom of "first submit doesn't work, but second submit works" indicates same problem as described in possible cause #9 here: http://stackoverflow.com/q/2118656, only your code snippet doesn't match that. Perhaps you oversimplified it without actually testing the so far shown code yourself? – BalusC Jul 18 '16 at 13:11

0 Answers0