JSF phases in XPages

I was trying to setup a system to handle “validation” via setup documents
the normal validators which can be set up directly on each field would not help much (or be too much work to implement)

I first tried a simple method: isDocValid returning true/false and adding JSF Messages like following:

String m1 = “summary: ups something went wrong”;
String m2 = “detail: ups something went wrong”;
msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, m1, m2);
facesContext.addMessage(field.getClientId(facesContext), msg);

I would call this methid in the querySaveEvent of the XPage, and it seemed (at least for new documents) to work well.. until I opened a document from a view, and oh wonder (maybe it’s a feature and not a bug) the querySaveDocument event was not even triggered…

That was bad news: I also had a “save as draft” button which would let the user return to the document, but if the validation then would fail?

nope.. I started looking for another way…

so I talked to a guy who has a LOT, I mean a LOOOOOOOOOOOOOT, of JSF experience.
With his help and some from my best friend (google) I did the following:

created a phase listener which logged all events (great for debugging, see those “immediate” events)
a very good article about the JSF phases can be found here:
http://www.ibm.com/developerworks/library/j-jsf3/
a MUST read for any serious XPages developer with little or no JSF experience!!

so then thanks to the phase listener I could see the how all phases process.

and now I immediately had also a way to “plugin” my custom validators properly (more on that in another post)

so here what I did for the phase listener:
Java class:

public class PhaseListener implements javax.faces.event.PhaseListener {

public void beforePhase(PhaseEvent event) {
// log your beforePhase with event.getPhaseId()
}

public void afterPhase(PhaseEvent event) {
// log your afterPhase with event.getPhaseId()
}
}

faces-config.xml:
  <lifecycle>
    <phase-listener>package.of.your.PhaseListener</phase-listener>
  </lifecycle>

now you can add simple additional code in any phase (before or after)
just use something like:

if (event.getPhaseId().equals(PhaseId.PROCESS_VALIDATIONS)) {
// run your code
}

PhaseId. (dot control space) will list all the CONSTANTS to check for any of the phases

Comments

  1. in the Notes Designer you have under the code area an option Java. This is the place where you put all your Java code.

    You might have to change the perspective though to make the "Java" container visible in the first place. As far as I remember (don't have a designer available right now) it's not available in the default perspective, switch to XPages perspective, then you should see the section right under the Script Libraries and WebServices

  2. Great approach… but: how do I generate a new validation error (aka ValidatorException) in the code in the listener method? I tried the following but no luck (error is not displayed in the messages control):

    public void beforePhase(PhaseEvent event) {
    if(event.getPhaseId().equals(PhaseId.PROCESS_VALIDATIONS)){
    FacesMessage msg = new FacesMessage("error");
    throw new ValidatorException(msg);
    }
    }

  3. Hi Oliver

    it's been a while since I wrote that blog. the phase listener was only used to activate/deactivate the validators.

    the actual validators (how to use them) you can find in this blog (http://in-mood.blogspot.ch/2012/09/sick-of-adding-hundres-of-standard.html)

    but in the end I didn't implement this approach for our final solution!

    Reason: it would not allow to handle "un-visited" tabs. Ie. I couldn't validate fields on a tab which has never been visited. this was crucial in our application.

    Unvisited tabs are not part of the component "tree" and thus simply do not exist.

    what we came up with:
    behind the "save" buttons we put an action group with a condition return Bean.isValid()
    the Bean itself then runs on all the "defined" field validations and "constructs" all the validation messages with something like this:

    FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, field.errorDetail, field.errorSummary);
    FacesContext context = FacesContext.getCurrentInstance();
    context.addMessage(null, msg);

Leave a Reply

Your email address will not be published. Required fields are marked *


The reCAPTCHA verification period has expired. Please reload the page.