1 2 Previous Next 15 Replies Latest reply on Jan 26, 2007 9:21 AM by gavin.king

    Customize validation messages

      Hi,

      I´m currently doing my first steps with Seam and JSF and, as a long-time Struts-developer, I already see so many improvements in the Seam/EJB3/JSF integration. Thank you for the great work!
      But now I´m starting at a very basic problem: validation!
      The struts way of life was that you have a validate()-method where you have to validate everything and if an error occured you can assign a customized error-message to an input-field that was localized through the RessourceBundle.

      If I do the validation through jsf and the hibernate validation framework I get very unsexy error-messages like ""name": Value is required." if I set required = true and the field was left blank or "has to be between 3 and 2147483647 " or I get an output og my complete regexp if not left blank and the hibernate validation fails. What I would like to do is to show customized error-messages like "Please enter a valid email-adress" or "The password has to have at least 6 characters and 1 number" etc. One thing I found regarding customizing messages was redefining the properties like "javax.faces.component.UIInput.REQUIRED" in the message.properties. But this way the message cannot be customized for every single input-field. So what would be the right way to do in this case? Do I have to write my own validator and tag-library for every inputfield to archieve this (only to display different but customized messages) or is there an other - more elegant and less complex - way?

      Thanks for your help

      Thomas

        • 1. Re: Customize validation messages

          Inside the hibernate-annotations.jar file, under org/hibernate/validator/resources, there are numerous property files.
          i.e. DefaultValidatorMessages.properties

          Just override those key values in your own messages.properties.

          As well, there's a message attribute with most, if not all of the hibernate annotations that you can override.

          • 2. Re: Customize validation messages

            The validation annotations themselves have a place for a validation message. You can use either a custom message for the field or a resource to look for in your resource bundle.

            • 3. Re: Customize validation messages

              Thanks for your help! Norman, your solution worked perfectly as I wanted it to be, besides the fact that you have to use a slightly different annotation if you want to use messages fro the seam resourceBundle which caused me some confusions until I realized that fact. So if anyone else has the same problem use this syntax as an example:

              @Length(min=3, message="#{messages['validation.user.name.length']}")


              But I´d like to extend this topic a little bit, because I have some more questions about that. Unfortunately the hibernate validation were not used if the required=true attribute is missing in the jsf-tag. Although I´d say that for example a @Length(min=3) annotation also means that it should not be empty and for some reason the @NotEmpty-annotation is not supported with the current seam-release (is there a reason?).
              So I have to add the required="true" attribute to every input field to work with the hibernate validation. Is there also a possibility to customize the "field-is-required"-messages for every input field, so it is not the standard-message from "javax.faces.component.UIInput.REQUIRED" for every field?

              I saw, that sun uses a syntax like in the following code-snippet, to provide this functionality but it doesn´t seem to be a standard-jsf-tag:

              <h:inputText id="ccno" size="19"
               required="true"
               requiredMessage="#{customMessages.ReqMessage}" >
               ...
              </h:inputText>
              <h:message styleClass="error-message" for="ccno"/>


              Furthermore I get an error while trying to use the sun jsf-impl, whereas myfaces works perfectly. But that´s probably another topic.

              So is there also a solution to this problem?
              Thank you very much for your help

              Thomas

              • 4. Re: Customize validation messages
                waheed.murad

                i have also faces the same problem? i have to also go for a same solution "required=true" instead of @NotNull. is it some thing wrong with this anotation if not then for what exactly it is for...

                • 5. Re: Customize validation messages

                  I think this is a real pain point for people. I through together a quick proof of concept. It works with MyFaces, but it shouldn't take that much to make a proper component out of. If anyone is ambitious enough, have ago at it:

                  package org.jboss.seam.ui;
                  
                  import javax.faces.component.*;
                  import javax.faces.context.FacesContext;
                  import javax.faces.application.FacesMessage;
                  
                  import org.jboss.seam.core.*;
                  
                  public class UIRequiredInput
                   extends UIInput
                  {
                   String requiredMessage;
                  
                   public void setRequiredMessage(String requiredMessage) {
                   this.requiredMessage = requiredMessage;
                   }
                  
                   public String getRequiredMessage() {
                   return requiredMessage;
                   }
                  
                   @Override
                   public boolean isRequired()
                   {
                   return true;
                   }
                  
                   @Override
                   protected void validateValue(FacesContext context, Object convertedValue)
                   {
                   System.out.println("*CHECKING! " + convertedValue);
                   if (empty(convertedValue)) {
                   context.addMessage(getClientId(context),
                   new FacesMessage(FacesMessage.SEVERITY_ERROR,
                   requiredMessage,
                   requiredMessage));
                   System.out.println("*INVALID! " + convertedValue);
                   setValid(false);
                   } else {
                   super.validateValue(context, convertedValue);
                   }
                   }
                  
                   protected boolean empty(Object value) {
                   if (value == null) {
                   return true;
                   }
                  
                   if (value instanceof String) {
                   String text = (String) value;
                   if (text.length() == 0) {
                   return true;
                   }
                   }
                  
                   return false;
                   }
                  
                   @Override
                   public void restoreState(FacesContext context, Object state) {
                   Object[] values = (Object[]) state;
                   super.restoreState(context, values[0]);
                   requiredMessage = (String) values[1];
                   }
                  
                   @Override
                   public Object saveState(FacesContext context) {
                   Object[] values = new Object[2];
                   values[0] = super.saveState(context);
                   values[1] = requiredMessage;
                   return values;
                   }
                  
                  }
                  


                  Usage:

                  <s:input id="payee" value="#{newPayment.payee}"
                   requiredMessage="You have to send a payment TO somebody" />



                  This shouldn't be taken as saying this will ever become a proper seam component. I just put it in the ui package because it was easier to get started with.



                  • 6. Re: Customize validation messages

                    Norman,

                    can you please give a little bit more detail about using your code. Simply creating the class and using the s:input-tag leads to an exception. Sorry - but I haven´t done anything in the tag-libraries yet, so I´m not so familiar how to add a new Tag.

                    I get the following exception:

                    com.sun.facelets.tag.TagException: /WEB-INF/pages/registerUser/userDataForm.xhtml @53,142 <s:input> Tag Library supports namespace: http://jboss.com/products/seam/taglib, but no tag was defined for na
                    me: input
                     at com.sun.facelets.compiler.CompilationManager.pushTag(CompilationManager.java:193)
                     at com.sun.facelets.compiler.SAXCompiler$CompilationHandler.startElement(SAXCompiler.java:194)
                     at org.apache.xerces.parsers.AbstractSAXParser.startElement(Unknown Source)
                     at org.apache.xerces.parsers.AbstractXMLDocumentParser.emptyElement(Unknown Source)
                     at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanStartElement(Unknown Source)
                     at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
                     at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
                     at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
                     at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
                     at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
                     at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
                     at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
                     at javax.xml.parsers.SAXParser.parse(Unknown Source)
                     at javax.xml.parsers.SAXParser.parse(Unknown Source)
                     at com.sun.facelets.compiler.SAXCompiler.doCompile(SAXCompiler.java:232)
                     at com.sun.facelets.compiler.Compiler.compile(Compiler.java:104)
                     at com.sun.facelets.impl.DefaultFaceletFactory.createFacelet(DefaultFaceletFactory.java:192)
                     at com.sun.facelets.impl.DefaultFaceletFactory.getFacelet(DefaultFaceletFactory.java:141)
                     at com.sun.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:293)
                     at com.sun.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:273)
                     at com.sun.facelets.impl.DefaultFaceletContext.includeFacelet(DefaultFaceletContext.java:143)
                     at com.sun.facelets.tag.ui.IncludeHandler.apply(IncludeHandler.java:60)
                     at com.sun.facelets.tag.ui.DefineHandler.apply(DefineHandler.java:58)
                     at com.sun.facelets.tag.ui.CompositionHandler.apply(CompositionHandler.java:128)
                     at com.sun.facelets.impl.DefaultFaceletContext$TemplateManager.apply(DefaultFaceletContext.java:306)
                     at com.sun.facelets.impl.DefaultFaceletContext.includeDefinition(DefaultFaceletContext.java:279)
                     at com.sun.facelets.tag.ui.InsertHandler.apply(InsertHandler.java:68)
                     at com.sun.facelets.tag.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:47)
                     at com.sun.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:49)
                     at com.sun.facelets.tag.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:47)
                     at com.sun.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:25)
                     at com.sun.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:248)
                     at com.sun.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:294)
                     at com.sun.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:273)
                     at com.sun.facelets.impl.DefaultFaceletContext.includeFacelet(DefaultFaceletContext.java:143)
                     at com.sun.facelets.tag.ui.CompositionHandler.apply(CompositionHandler.java:113)
                     at com.sun.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:49)
                     at com.sun.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:25)
                     at com.sun.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:95)
                     at com.sun.facelets.FaceletViewHandler.buildView(FaceletViewHandler.java:510)
                     at com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:553)
                     at org.ajax4jsf.framework.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:101)
                     at org.ajax4jsf.framework.ajax.AjaxViewHandler.renderView(AjaxViewHandler.java:222)
                     at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:384)
                     at javax.faces.webapp.FacesServlet.service(FacesServlet.java:138)
                     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
                     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                     at org.ajax4jsf.framework.ajax.xmlfilter.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:75)
                     at org.ajax4jsf.framework.ajax.xmlfilter.BaseFilter.doFilter(BaseFilter.java:213)
                     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                     at org.jboss.seam.servlet.SeamRedirectFilter.doFilter(SeamRedirectFilter.java:32)
                     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                     at org.jboss.seam.servlet.SeamExceptionFilter.doFilter(SeamExceptionFilter.java:46)
                     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                     at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
                     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)


                    Thanks a lot.

                    By the way: I think that would be a great benefit for seam to have this component, so why don´t you make it a core-feature? I think it´s a very common and important thing to display user-friendly validation-messages. You should definately think about it

                    Thomas

                    • 7. Re: Customize validation messages
                      agori

                       

                      "hamtho2" wrote:

                      By the way: I think that would be a great benefit for seam to have this component, so why don´t you make it a core-feature? I think it´s a very common and important thing to display user-friendly validation-messages. You should definately think about it
                      Thomas


                      If you need, JSF RI (1.2) has already the requiredMessage attribute...

                      • 8. Re: Customize validation messages
                        pmuir

                        Trinidad supports this as well.

                        • 9. Re: Customize validation messages
                          spambob

                          +1 on the idea that stuff like @Length(min=3) triggers a validation error although required=true is missing if the field is empty! Is there an actual reason that this isn't the way it behaves?

                          For the people who want custom validation messages for certain input fields: http://www.oracle.com/technology/pub/articles/masterj2ee/j2ee_wk7.html (the "Adding Meaningful Field References" section) describes a way doing this by using a PhaseListener that customizes the validation messages based on parameter supplied by f:attribute tags inside the h:input stuff.

                          Does anyone now if the above approach is performance expensive?

                          • 10. Re: Customize validation messages
                            gavin.king

                             

                            Is there an actual reason that this isn't the way it behaves?


                            We are limited by the JSF spec.

                            • 11. Re: Customize validation messages

                              So what would you guys say: which jsf-implementation works better for seam? Right now we have the Sun JSF RI, Trinidad and myFaces (which I´m currently working with). In the seam documentation it is said, that trinidad is the better choice. Are there any major differences or any deviation from the standard specification between the alternatives, that should be kept in mind? What about future development? Do I have to be afraid that one of them won´t be developed furthermore?

                              What are your experiences? Right now I´m at the very beginning of development, so it´s still time to change. I don´t wanna realize that I did the wrong choice when it´s too late and I have to do a lot of rework then.

                              Thanks for sharing your experiences

                              Thomas

                              • 12. Re: Customize validation messages
                                pmuir

                                Choice of JSF impl is the RI or myfaces. Icefaces, tomahawk, trinidad are component libraries.

                                • 13. Re: Customize validation messages

                                  Thanks for that - I guess that wasn´t too clear for me.
                                  So a component-library like Trinidad also uses a standard-jsf component like inputText, but extends them without keeping the jsf-specifications? So I´m kind of stucked to one component-library if I choose to use one?

                                  • 14. Re: Customize validation messages
                                    pmuir

                                    1) Yes.

                                    2) Your stuck with one JSF impl but not component lib (but you may well find that they aren't that compatible!) - I know that a target of Trinidad is that it will be completely compatible with Tomahawk. And Seam's seam-ui , seam-pdf and seam-mail are component libraries and will (mostly) play nicely with any other component set. I use a couple internally as well (JSF versions of DOJO and support components).

                                    1 2 Previous Next