13 Replies Latest reply on Oct 31, 2009 8:29 PM by sambp

    Seam custom validator

    lasansue.jeremy.girard.satives.fr

      Hi,


      I've seen in the reference manual that it is possible to write custom validator and to use it as annotation.


      The given example is @ZipCode (chapter 9).


      To be a validator, a seam component need to have the @Validator and to implement javax.faces.validator.Validator.


      But what i don't understand, it's how to code the validator to be used as an annotation.


      If someone could give me an example (as @zipcode) it would be helping me a lot.


      Thanks


      Jeremy

        • 1. Re: Seam custom validator
          wrzep

          jeremy girard wrote on Mar 11, 2008 04:47 PM:

          But what i don't understand, it's how to code the validator to be used as an annotation.

          If someone could give me an example (as @zipcode) it would be helping me a lot.


          Have a look at the
          Hibernate Validator documentation.


          Cheers,
          -Pawel

          • 2. Re: Seam custom validator

            The docs are good at the pointer he gave you.  Here's one I wrote so that I can have a pattern validator that doesn't care if the value is blank.


            It'd be nice if there were a central repository to share validators since they're so common?  Guess I should ask that on the hibernate list...


            -mp



            import java.lang.annotation.Retention;
            import java.lang.annotation.*;
            
            import org.hibernate.validator.ValidatorClass;
            
            @ValidatorClass(OptionalPatternValidator.class)
            @Target({ElementType.METHOD, ElementType.FIELD})
            @Retention(RetentionPolicy.RUNTIME)
            public @interface OptionalPattern {
            /** regular expression */
            String regex();
            
            /** regular expression processing flags */
            int flags() default 0;
            
            String message() default "{validator.pattern}";
            }
            





            import java.io.Serializable;
            import java.util.regex.Matcher;
            
            import org.hibernate.validator.Pattern;
            import org.hibernate.validator.PatternValidator;
            import org.hibernate.validator.Validator;
            
            public class OptionalPatternValidator  implements Validator<OptionalPattern>, Serializable {
            
                 private java.util.regex.Pattern pattern;
            
                 public void initialize(OptionalPattern parameters) {
                 pattern = java.util.regex.Pattern.compile(
                 parameters.regex(),
                 parameters.flags()
                 );
                 }
            
                 public boolean isValid(Object value) {
                 if ( value == null ) return true;
                 if ( !( value instanceof String ) ) return false;
                 String string = (String) value;
                 if(string.length() == 0) return true;
                 Matcher m = pattern.matcher( string );
                 return m.matches();
                 }
            
            }
            
            


            • 3. Re: Seam custom validator
              lasansue.jeremy.girard.satives.fr

              Thank you both for your replies.


              I will try it tomorrow ...

              • 4. Re: Seam custom validator
                kariem

                It'd be nice if there were a central repository to share validators since they're so common? Guess I should ask that on the hibernate list...

                Marcus, if you could add the link to your post to the hibernate list here, I would be very grateful. It did not take long for me to implement a custom validator, but I think the documentation is not really sufficient, and some examples really help.


                Going to share my validator here. You can use it to enforce a password policy:


                @Length(min = 8, max = 20)
                @PasswordPolicyRestricted(minDigits = 1, minAlphas = 1)
                String newPassword;



                Declaration:


                import java.lang.annotation.Documented;
                import java.lang.annotation.Retention;
                import java.lang.annotation.Target;
                
                import static java.lang.annotation.ElementType.*;
                import static java.lang.annotation.RetentionPolicy.*;
                
                import org.hibernate.validator.ValidatorClass;
                
                @ValidatorClass(PasswordPolicyRestrictedValidator.class)
                @Target({FIELD, METHOD})
                @Retention(RUNTIME)
                @Documented
                public @interface PasswordPolicyRestricted {
                  /** @return minimum number of characters from a-z (lower or upper case) */
                  int minAlphas() default 0;
                
                  /** @return minimum number of digits */
                  int minDigits() default 0;
                
                  /** @return error message key */
                  String message() default "{validator.password_policy}";
                }



                Implementation:


                import java.lang.annotation.Annotation;
                import java.util.regex.Matcher;
                import java.util.regex.Pattern;
                
                import org.hibernate.validator.Validator;
                
                public class PasswordPolicyRestrictedValidator implements
                    Validator<PasswordPolicyRestricted> {
                
                  Pattern ALPHA = Pattern.compile("[a-zA-Z]");
                  Pattern DIGIT = Pattern.compile("\\d");
                
                  private PasswordPolicyRestricted policy;
                
                  /** @see Validator#initialize(Annotation) */
                  public void initialize(PasswordPolicyRestricted passwordPolicy) {
                    this.policy = passwordPolicy;
                  }
                
                  public boolean isValid(Object o) {
                    if (o == null) {
                      return false;
                    }
                    if (o instanceof String) {
                      return isValid((String) o);
                    }
                    return isValid(o.toString());
                  }
                
                  /**
                   * @param password
                   *            the password to check
                   * @return whether password is valid according to its annotation
                   */
                  public boolean isValid(String password) {
                    return check(password, policy.minAlphas(), ALPHA)
                        && check(password, policy.minDigits(), DIGIT);
                  }
                
                  /**
                   * @param sequence
                   *            the sequence to check
                   * @param min
                   *            minimum number of {@code pattern}
                   * @param pattern
                   *            the pattern to check
                   */
                  private boolean check(String sequence, int min, Pattern pattern) {
                    if (min > 0) {
                      Matcher m = pattern.matcher(sequence);
                      int count = 0;
                      while (m.find()) {
                        count++;
                      }
                      if (min > count) {
                        return false;
                      }
                    }
                    return true;
                  }
                }



                Don't forget that you can leverage the seam infrastructure for i18n (Marcus also used it in his example) by declaring the message() method in the interface. With this, you can define the message in your .properties file:


                validator.password_policy=Password policy: minimum digits: {minDigits}; minimum letters: {minAlphas}

                • 5. Re: Seam custom validator
                  lasansue.jeremy.girard.satives.fr

                  I managed to make a custom validator but there is something more.
                  How can you have a non null EntityManager in your validator impl ?


                  Here is my validator.
                  Entity manager is always null.


                  @Name("uniqueEntityValidator")
                  @Stateless
                  @JndiName(value="blog/UniqueEntityValidator/local")
                  public class UniqueEntityValidator implements Validator<UniqueEntity>, Serializable, Test  {
                       
                       /**
                        * 
                        */
                       private static final long serialVersionUID = 1L;
                       
                       private String targetEntity;
                       private String field;
                       
                       @In(create=true)
                       EntityManager entityManager;
                       
                  
                       public void initialize(UniqueEntity parameters) {
                            targetEntity = parameters.targetEntity();
                            field = parameters.field();
                       }
                  
                       public boolean isValid(Object value) {
                            Query query = entityManager.createQuery("From "+targetEntity+" where "+field+" = :value");
                            query.setParameter("value", value);
                            
                            try{
                                 query.getSingleResult();
                                 return false;
                            }catch(final NoResultException e){
                                 return true;
                            }
                       }
                  }




                  I need it to perform a validation on unique constraint.


                  Thanks

                  • 6. Re: Seam custom validator
                    kariem

                    I think this should work the way you wrote it. Your entity manager has the name entityManager?


                    Did you try method-level injection via components.xml (I am not sure if that works for session beans, though)?


                    Recently, I had a similar issue, but I solved it with a different approach. Nobody commented yet, so I don't really know, whether this is a feasible solution for you. You need to have the following set up for this solution to work (if it does not work with @In or method-level injection):



                    • Use entity home objects

                    • Use hibernate session



                    In addition, the processing occurs in invoke application (EntityHome.save() and EntityHome.update()) instead of the validation phase.


                    I can share the code, but as I said, nobody gave feedback and I consider it rather a hack.

                    • 7. Re: Seam custom validator
                      lasansue.jeremy.girard.satives.fr

                      Yes my entitymanager is named entitymanager.


                      I haven't tried by component.xml.
                      I think i gonna do this in a regular seam component (registerAction)


                      • 8. Re: Seam custom validator

                        shouldn't you use @PersistenceContext in EJBs?

                        • 9. Re: Seam custom validator
                          lasansue.jeremy.girard.satives.fr

                          I tried, but don't work -> null ...

                          • 10. Re: Seam custom validator
                            fernando_jmt

                            Have you tried something like?:



                            @Name("uniqueEntityValidator")
                            @Stateless
                            @JndiName(value="blog/UniqueEntityValidator/local")
                            public class UniqueEntityValidator implements Validator<UniqueEntity>, Serializable, Test  {
                                 
                                 /**
                                  * 
                                  */
                                 private static final long serialVersionUID = 1L;
                                 
                                 private String targetEntity;
                                 private String field;
                                 
                                 
                            
                                 public void initialize(UniqueEntity parameters) {
                                      targetEntity = parameters.targetEntity();
                                      field = parameters.field();
                                 }
                            
                                 public boolean isValid(Object value) {
                                      Query query = ((EntityManager) Component.getInstance("entityManager")).createQuery("From "+targetEntity+" where "+field+" = :value");
                                      query.setParameter("value", value);
                                      
                                      try{
                                           query.getSingleResult();
                                           return false;
                                      }catch(final NoResultException e){
                                           return true;
                                      }
                                 }
                            }


                            • 11. Re: Seam custom validator
                              lasansue.jeremy.girard.satives.fr

                              I tried, null !
                              But it works fine with a business validation instead of hibernate validation

                              • 12. Re: Seam custom validator
                                sambp

                                I'm having the same problem. I'm using a Hibernate Session, and he's always null. But it works fine with a business validation too. Someone fix that? Sorry my English.

                                • 13. Re: Seam custom validator
                                  sambp

                                  I solve the @In problem using:


                                  Session session = (Session) Component.getInstance("session");



                                  But now when I try to saveOrUpdate I got another exception:


                                  java.lang.reflect.InvocationTargetException
                                  javax.faces.el.EvaluationException: java.lang.reflect.InvocationTargetException
                                          at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:91)
                                          at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:91)
                                          at javax.faces.component.UICommand.broadcast(UICommand.java:383)
                                          at org.ajax4jsf.component.AjaxActionComponent.broadcast(AjaxActionComponent.java:55)
                                          at org.ajax4jsf.component.AjaxViewRoot.processEvents(AjaxViewRoot.java:321)
                                          at org.ajax4jsf.component.AjaxViewRoot.broadcastEvents(AjaxViewRoot.java:296)
                                          at org.ajax4jsf.component.AjaxViewRoot.processPhase(AjaxViewRoot.java:253)
                                          at org.ajax4jsf.component.AjaxViewRoot.processApplication(AjaxViewRoot.java:466)
                                          at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:97)
                                          at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:251)
                                          at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:117)
                                          at javax.faces.webapp.FacesServlet.service(FacesServlet.java:244)
                                          at org.apache.catalina.core.ApplicationFilterChain.servletService(ApplicationFilterChain.java:427)
                                          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:333)
                                          at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
                                          at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:83)
                                          at org.jboss.seam.web.IdentityFilter.doFilter(IdentityFilter.java:40)
                                          at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                                          at org.jboss.seam.web.MultipartFilter.doFilter(MultipartFilter.java:90)
                                          at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                                          at org.jboss.seam.web.ExceptionFilter.doFilter(ExceptionFilter.java:64)
                                          at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                                          at org.jboss.seam.web.RedirectFilter.doFilter(RedirectFilter.java:45)
                                          at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                                          at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:178)
                                          at org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:290)
                                          at org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:390)
                                          at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:517)
                                          at org.jboss.seam.web.Ajax4jsfFilter.doFilter(Ajax4jsfFilter.java:56)
                                          at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                                          at org.jboss.seam.servlet.SeamFilter.doFilter(SeamFilter.java:158)
                                          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:246)
                                          at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
                                          at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:313)
                                          at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:287)
                                          at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:218)
                                          at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
                                          at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
                                          at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:94)
                                          at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:98)
                                          at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:222)
                                          at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
                                          at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
                                          at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:587)
                                          at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1096)
                                          at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:166)
                                          at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
                                          at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
                                          at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:587)
                                          at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1096)
                                          at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:288)
                                          at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:647)
                                          at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.doProcess(DefaultProcessorTask.java:579)
                                          at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.process(DefaultProcessorTask.java:831)
                                          at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.executeProcessorTask(DefaultReadTask.java:341)
                                          at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:263)
                                          at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:214)
                                          at com.sun.enterprise.web.connector.grizzly.TaskBase.run(TaskBase.java:265)
                                          at com.sun.enterprise.web.connector.grizzly.ssl.SSLWorkerThread.run(SSLWorkerThread.java:106)
                                  Caused by: java.lang.reflect.InvocationTargetException
                                          at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                                          at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                                          at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                                          at java.lang.reflect.Method.invoke(Method.java:597)
                                          at org.jboss.seam.util.Reflections.invoke(Reflections.java:22)
                                          at org.jboss.seam.intercept.RootInvocationContext.proceed(RootInvocationContext.java:32)
                                          at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:56)
                                          at org.jboss.seam.transaction.RollbackInterceptor.aroundInvoke(RollbackInterceptor.java:28)
                                          at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                                          at org.jboss.seam.core.BijectionInterceptor.aroundInvoke(BijectionInterceptor.java:77)
                                          at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                                          at org.jboss.seam.core.MethodContextInterceptor.aroundInvoke(MethodContextInterceptor.java:44)
                                          at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                                          at org.jboss.seam.intercept.RootInterceptor.invoke(RootInterceptor.java:107)
                                          at org.jboss.seam.intercept.JavaBeanInterceptor.interceptInvocation(JavaBeanInterceptor.java:185)
                                          at org.jboss.seam.intercept.JavaBeanInterceptor.invoke(JavaBeanInterceptor.java:103)
                                          at fachada.VerbeteAction_$$_javassist_seam_3.salvarVerbete(VerbeteAction_$$_javassist_seam_3.java)
                                          at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                                          at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                                          at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                                          at java.lang.reflect.Method.invoke(Method.java:597)
                                          at org.jboss.el.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:335)
                                          at org.jboss.el.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:348)
                                          at org.jboss.el.parser.AstPropertySuffix.invoke(AstPropertySuffix.java:58)
                                          at org.jboss.el.parser.AstValue.invoke(AstValue.java:96)
                                          at org.jboss.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:276)
                                          at com.sun.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:68)
                                          at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:77)
                                          ... 58 more
                                  Caused by: java.lang.StackOverflowError
                                          at org.jboss.seam.contexts.Contexts.isMethodContextActive(Contexts.java:89)
                                          at org.jboss.seam.contexts.Contexts.lookupInStatefulContexts(Contexts.java:157)
                                          at org.jboss.seam.Component.getInstance(Component.java:1982)
                                          at org.jboss.seam.Component.getInstance(Component.java:1977)
                                          at org.jboss.seam.Component.getInstance(Component.java:1972)
                                          at validacao.palavra.ValidadorPalavraUnica.isValid(ValidadorPalavraUnica.java:39)
                                          at validacao.palavra.ValidadorPalavraUnica.isValid(ValidadorPalavraUnica.java:32)
                                  ......etc