Valter Silva
Valter Silva

Reputation: 16656

How to return status messages in JSF?

I will make myself pretty objective:

An user register himself, after this operation register I would like to show some message saying Congratulations! Welcome!, something like that or some message if there's some problem like duplicated key in my database.

I already know how to show some messages with RichFaces, but I want to keep it simple, RichFaces messes up a little with CSS so I want to avoid it for now.

So here it goes my page and my managed beans:

    <h:form id="form_user">
        <h:panelGrid columns="3">
            <h:outputLabel for="name" value="Name:" />
            <h:inputText id="name" value="#{personc.personb.person.name}">
                <f:ajax event="blur" listener="#{personValidate.name}" render="m_name" />
            </h:inputText>
            <h:message id="m_name" for="name" />

            <!--other fields...-->

            <h:commandButton value="Register" action="#{personc.register}">
                <f:ajax execute="@form" render="@form" />
            </h:commandButton>

        </h:panelGrid>
    </h:form>

Person Controller (following the MVC structure):

@ManagedBean(name="personc")
@SessionScoped
public class PersonController implements Serializable {
    private static final long serialVersionUID = 2000186666864113813L;

    // attributes
    @EJB PersonEAO personEAO;
    private PersonBean personb;

    public PersonController() {
        personb = new PersonBean();
    }

    // methods
    public String register(){
        if (personEAO.register(personb.getUser(), personb.getPerson())){
            status = "welcome!";
            return "success";
        }else{
            status = "an error occurs";
            return "failure";
        }
    }
        // others methods..

And the PersonEAO entity:

@Stateless
@LocalBean
public class PersonEAO {
    @PersistenceContext
    EntityManager em;

    public PersonEAO() {}

    public boolean register(User user, Person person){
        try{
            em.persist(user);

            person.setUser(user);
            em.persist(person);
        }catch(Exception e){
                    // would like to treat better the exceptions here 
                    // should I keep returning boolean here ? or a string would be better ? 
            return false;
        }
        return true;
    }

I have some questions :

If I should keep returning boolean from operations with JPA, I would like to give a better message to the user saying what happened, like duplicate key, for instance.

Upvotes: 5

Views: 2323

Answers (2)

BalusC
BalusC

Reputation: 1108742

Don't use boolean flags (or enums) to indicate exceptional results. Use exceptions for this. You only need to add some logic what kind of JPA implementation specific exception the PersistenceException is wrapping and then rethrow it as your specific business exception, e.g. a custom public class DuplicateKeyException extends PersistenceException.

For example, in your business service implementation

public void register(User user, Person person) throws PersistenceException, DuplicateKeyException {
    try {
        em.persist(user);
        person.setUser(user);
        em.persist(person);
    }
    catch (PersistenceException e) {
        if (Exceptions.is(e, ConstraintViolationException.class)) {
            throw new DuplicateKeyException(e);
        }
        else {
            throw e;
        }
    }
}

with this method in the Exceptions utility class:

public static <T extends Throwable> boolean is(Throwable exception, Class<T> type) {
    for (;exception != null; exception = exception.getCause()) {
        if (type.isInstance(exception)) {
            return true;
        }
    }

    return false;
}

Which you then handle in your controller side:

public String register() {
    try {
        personEAO.register(personb.getUser(), personb.getPerson());
        Messages.addGlobalInfo("user.register.success");
        return "success";
    }
    catch (DuplicateKeyException e) {
        logger.log(e); // If necessary.
        Messages.addGlobalError("user.register.error.duplicate");
        return "failure";
    }
}

The Messages.addGlobalXxx() could add a global FacesMessage to the context, like as follows

FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(
    FacesMessage.SEVERITY_INFO, someBundle.getString(key), null));

OmniFaces has a similar class which may aid you in here.

Such a global message will only show up in a

<h:messages globalOnly="true" />

Upvotes: 2

Daniel
Daniel

Reputation: 37061

How about creating an enum with predefined values and instead of catch(Exception e) do several more specific catch's and in each on of them set the retrun parameter of the method with that specific enum value

enum example

public enum REGISTERCODE{
    SUCCESS , DUPLICATE, GENERAL_ERROR, SESSION_EXPIRED, MORE, MORE2; 
}

change signature into something like

public REGISTERCODE register(User user, Person person){...return REGISTERCODE.SUCCESS...

also.. you can create key in your language file with the prefix ResgisterCode_ and then return messages to your user based on this, like

localizationClass.getMessageFromBundle("ResgisterCode_"+REGISTERCODE_variable.name())

where REGISTERCODE_variable is the return value from register method

then in the caller method in your personc bean return a detailed message based on the enum value...

Upvotes: 1

Related Questions