12 Replies Latest reply on Jul 30, 2008 6:57 PM by vineetmanohar

    Problem with Validator

    anikanchan

      I am trying to use Hibernate custom validator and I am having problem with that. My validator method looks like below:



      
      public boolean isValid(Object object) {
      
               System.out.println("PublicationStatusValidator.isValid()");
      
              NewsArticle content = (NewsArticle) object;
      
              if(content.getStatus().equals(PublicationStatus.PUBLISHED)) {
      
                  if (content.getTitle() != null &&content.getTitle().trim().length() > 0 && 
      
                            content.getSummary() != null && content.getSummary().trim().length() > 0 && 
      
                            content.getBody() != null && content.getBody().trim().length() > 0 && 
      
                            content.getPublicationDate() != null ) {
      
                      return true;
      
                  }
      
                  return false;
      
              }
      
              else if(content.getStatus().equals(PublicationStatus.DRAFT)) {
      
                   if (content.getTitle() != null &&content.getTitle().trim().length() > 0 ){
      
                         return true;
      
                   }
      
                   return false;
      
              }
      
              return true;
      
          }
      
      



      This method works fine by identifying the valid scenarios and returns true or false. If it returns true, everything works fine but as soon as it returns false, my application breaks giving IllegalStateException. My entity class is annotated as follows:


      
      @Entity
      
      @Name("newsArticle")
      
      @Table(name = "vew_news_article")
      
      @org.hibernate.annotations.Entity(dynamicInsert = true, dynamicUpdate = true, optimisticLock = OptimisticLockType.VERSION)
      
      @AttributeOverrides({
      
          @AttributeOverride(name = "lastModifiedBy",
      
                             column = @Column(name = "nwa_last_modified_by")),
      
          @AttributeOverride(name = "lastModified",
      
                             column = @Column(name = "nwa_last_modified_datetime")),
      
          @AttributeOverride(name = "createdBy",
      
                             column = @Column(name = "nwa_created_by")),
      
          @AttributeOverride(name = "created",
      
                             column = @Column(name = "nwa_created_datetime")),
      
          @AttributeOverride(name = "title",
      
                             column = @Column(name = "nwa_title")),
      
          @AttributeOverride(name = "body",
      
                             column = @Column(name = "nwa_body")),
      
          @AttributeOverride(name = "visibleOnHomePage",
      
                             column = @Column(name = "nwa_homepage_flag")),
      
          @AttributeOverride(name = "publicationDate",
      
                             column = @Column(name = "nwa_publication_date")),
      
          @AttributeOverride(name = "status",
      
                                     column = @Column(name = "nwa_publication_status")),
      
          @AttributeOverride(name = "version",
      
                                     column = @Column(name = "nwa_version"))
      
      })
      
      
      @NamedQueries( { 
      
          @NamedQuery(name = "findAllActiveNews", 
      
                      query = "from NewsArticle as nws where " +
      
             "nws.status = 'PUBLISHED' order by nws.publicationDate asc") })
      
      @PublicationCheck
      
      public class NewsArticle extends AbstractContent {
      
      //...
      
      }



      Can someone please tell me what I am doing wrong? Thanks.

        • 1. Re: Problem with Validator
          nickarls

          Full stack trace, please.

          • 2. Re: Problem with Validator
            anikanchan

            Sure, here it is. publish is my method on Seam controller that calls save object method in service class. Surprisingly, this method execution is completed successfully.




            DEBUG NewsAdminAction - Entering publish()
            DEBUG NewsAdminAction - Exiting publish()
            PublicationStatusValidator.isValid()
            ERROR SeamPhaseListener - uncaught exception
            java.lang.IllegalStateException: Could not commit transaction
                    at org.jboss.seam.jsf.SeamPhaseListener.commitOrRollback(SeamPhaseListener.java:602)
                    at org.jboss.seam.jsf.SeamPhaseListener.handleTransactionsAfterPhase(SeamPhaseListener.java:330)
                    at org.jboss.seam.jsf.SeamPhaseListener.afterServletPhase(SeamPhaseListener.java:231)
                    at org.jboss.seam.jsf.SeamPhaseListener.afterPhase(SeamPhaseListener.java:182)
                    at com.sun.faces.lifecycle.Phase.handleAfterPhase(Phase.java:175)
                    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:114)
                    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
                    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:265)
                    at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:225)
                    at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:127)
                    at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:283)
                    at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
                    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)
                    at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:141)
                    at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:281)
                    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)
                    at weblogic.servlet.internal.RequestEventsFilter.doFilter(RequestEventsFilter.java:26)
                    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)
                    at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3212)
                    at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
                    at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:121)
                    at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:1983)
                    at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:1890)
                    at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1344)
                    at weblogic.work.ExecuteThread.execute(ExecuteThread.java:209)
                    at weblogic.work.ExecuteThread.run(ExecuteThread.java:181)
            Caused by: org.hibernate.validator.InvalidStateException: validation failed for: com.ssga.spdrs.entity.news.NewsArticle
                    at org.hibernate.validator.event.ValidateEventListener.validate(ValidateEventListener.java:148)
                    at org.hibernate.validator.event.ValidateEventListener.onPreUpdate(ValidateEventListener.java:177)
                    at org.hibernate.action.EntityUpdateAction.preUpdate(EntityUpdateAction.java:217)
                    at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:65)
                    at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:250)
                    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:234)
                    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:142)
                    at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
                    at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
                    at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
                    at org.jboss.seam.persistence.HibernateSessionProxy.flush(HibernateSessionProxy.java:181)
                    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:585)
                    at org.jboss.seam.ioc.spring.SeamManagedSessionFactoryBean$SeamManagedSessionHandler.invoke(SeamManagedSessionFactoryBean.
            java:297)
                    at $Proxy836.flush(Unknown Source)
                    at org.springframework.orm.hibernate3.SpringSessionSynchronization.beforeCommit(SpringSessionSynchronization.java:135)
                    at org.springframework.transaction.support.TransactionSynchronizationUtils.triggerBeforeCommit(TransactionSynchronizationU
            tils.java:48)
                    at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCommit(AbstractPlatformTransact
            ionManager.java:882)
                    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionMan
            ager.java:692)
                    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.ja
            va:678)
                    at org.jboss.seam.ioc.spring.SpringTransaction.commit(SpringTransaction.java:79)
                    at org.jboss.seam.jsf.SeamPhaseListener.commitOrRollback(SeamPhaseListener.java:592)
                    ... 25 more
            Mar 24, 2008 5:23:13 PM com.sun.facelets.compiler.TagLibraryConfig loadImplicit
            INFO: Added Library from: zip:C:/DEV/bea92/userprojects/domains/mydomain/./servers/AdminServer/tmp/WLuser/ssga-tools-ear-1/hqb5
            g7/war/WEB-INF/lib/jboss-seam-ui-2.0.1.GA.jar!/META-INF/s.taglib.xml
            Mar 24, 2008 5:23:13 PM com.sun.facelets.compiler.TagLibraryConfig loadImplicit
            INFO: Added Library from: zip:C:/DEV/bea92/user
            projects/domains/mydomain/./servers/AdminServer/tmp/WLuser/ssga-tools-ear-1/hqb5
            g7/war/WEB-INF/lib/jsf-facelets-1.1.14.jar!/META-INF/jsf-core.taglib.xml
            Mar 24, 2008 5:23:13 PM com.sun.facelets.compiler.TagLibraryConfig loadImplicit
            INFO: Added Library from: zip:C:/DEV/bea92/userprojects/domains/mydomain/./servers/AdminServer/tmp/WLuser/ssga-tools-ear-1/hqb5
            g7/war/WEB-INF/lib/jsf-facelets-1.1.14.jar!/META-INF/jsf-html.taglib.xml
            Mar 24, 2008 5:23:13 PM com.sun.facelets.compiler.TagLibraryConfig loadImplicit
            INFO: Added Library from: zip:C:/DEV/bea92/user
            projects/domains/mydomain/./servers/AdminServer/tmp/WLuser/ssga-tools-ear-1/hqb5
            g7/war/WEB-INF/lib/jsf-facelets-1.1.14.jar!/META-INF/jsf-ui.taglib.xml
            Mar 24, 2008 5:23:13 PM com.sun.facelets.compiler.TagLibraryConfig loadImplicit
            INFO: Added Library from: zip:C:/DEV/bea92/userprojects/domains/mydomain/./servers/AdminServer/tmp/WLuser/ssga-tools-ear-1/hqb5
            g7/war/WEB-INF/lib/jsf-facelets-1.1.14.jar!/META-INF/jstl-core.taglib.xml
            Mar 24, 2008 5:23:13 PM com.sun.facelets.compiler.TagLibraryConfig loadImplicit
            INFO: Added Library from: zip:C:/DEV/bea92/user
            projects/domains/mydomain/./servers/AdminServer/tmp/WLuser/ssga-tools-ear-1/hqb5
            g7/war/WEB-INF/lib/jsf-facelets-1.1.14.jar!/META-INF/jstl-fn.taglib.xml
            Mar 24, 2008 5:23:13 PM com.sun.facelets.compiler.TagLibraryConfig loadImplicit
            INFO: Added Library from: zip:C:/DEV/bea92/userprojects/domains/mydomain/./servers/AdminServer/tmp/WLuser/ssga-tools-ear-1/hqb5
            g7/war/WEB-INF/lib/richfaces-ui-3.1.4.SR1.jar!/META-INF/a4j.taglib.xml
            Mar 24, 2008 5:23:13 PM com.sun.facelets.compiler.TagLibraryConfig loadImplicit
            INFO: Added Library from: zip:C:/DEV/bea92/user
            projects/domains/mydomain/./servers/AdminServer/tmp/WLuser/ssga-tools-ear-1/hqb5
            g7/war/WEB-INF/lib/richfaces-ui-3.1.4.SR1.jar!/META-INF/ajax4jsf.taglib.xml
            Mar 24, 2008 5:23:13 PM com.sun.facelets.compiler.TagLibraryConfig loadImplicit
            INFO: Added Library from: zip:C:/DEV/bea92/userprojects/domains/mydomain/./servers/AdminServer/tmp/WLuser/ssga-tools-ear-1/hqb5
            g7/war/WEB-INF/lib/richfaces-ui-3.1.4.SR1.jar!/META-INF/rich.taglib.xml
            Mar 24, 2008 5:23:13 PM com.sun.facelets.compiler.TagLibraryConfig loadImplicit
            INFO: Added Library from: zip:C:/DEV/bea92/user
            projects/domains/mydomain/./servers/AdminServer/tmp/WLuser/ssga-tools-ear-1/hqb5
            g7/war/WEB-INF/lib/richfaces-ui-3.1.4.SR1.jar!/META-INF/richfaces.taglib.xml

            • 3. Re: Problem with Validator
              rmcdonough

              I am working with Anikanchan on this and the issue that the Hibernate validator is work, it's just not being handled by Seam. The validation is conditional and only evaluated when the NewsArticle enters a published state. By default, the fields are nullable, except when the it enters a state of published.


              Now, can get Seam to handle the validation in JSF when se set all of the form fields to required to true, but then this means that the fields are always required, which is not the behavior we want. We want to keep this validation in the domain, but we're having issues getting Seam to handle it in the UI.


              Ryan-

              • 4. Re: Problem with Validator
                dhinojosa

                Can you format this as code?

                • 5. Re: Problem with Validator
                  rmcdonough

                  Daniel,


                  I don't know that the stack trace is really valuable here as the issue that the Validator is being executed by Hibernate, as one would expect. However, we would like the validation to be performed by Seam as defined in Chapter 9:


                  http://docs.jboss.com/seam/2.0.1.GA/reference/en/html/validation.htmlk


                  However, Seam is not handing our custom validator in the UI. The stack that Nik posted shows that the validation fails, as it should, when session.saveOrUpdate() is called. While we do want this to happen, we expected this to happen a bit earlier and handled in the UI.


                  Please keep in mind that this validation is conditional and is only evaluated when the publication status of a NewsArticle is set  to PUBLISHED. We do not want fields such as body and summary to be not null, hence not required if the status is DRAFT.


                  Ryan-

                  • 6. Re: Problem with Validator
                    matt.drees

                    Anikanchan Raut wrote on Mar 24, 2008 07:31 PM:


                    @Entity
                    ...
                    @PublicationCheck
                    public class NewsArticle extends AbstractContent {
                    //...
                    }





                    Seam's model validator only runs against property validators, not class validators.


                    (Short reason: during the Process Validations phase, the attributes of a model object can't be updated. Updates should only happen in the Update Model phase, and only if there were no validation errors.  It turns out that property validators can be checked without actually setting the property on the model object, but class validators can't.)

                    • 7. Re: Problem with Validator
                      dan.j.allen

                      The proper way to do cross-field validation in JSF is to handle it in your action method. I know, this is sort of lame, but it is just how JSF was designed. As @Matt points out, the validations on fields are applied as each field is converted in the order they exist on the page (technically the order in the tree), making cross-field validation ill-suited in this phase.


                      So what you need to do is manually kick off the Hibernate Validator in your action method. If you look at the code for BaseSeamTest, you will find an example of this. Here is the excerpt:


                      ClassValidator validator = Validators.instance().getValidator(modelClass);
                      InvalidValue[] ivs = validator.getPotentialInvalidValues(property, value);
                      if (ivs.length > 0)
                      {
                         validationFailed = true;
                         FacesMessage message = FacesMessages.createFacesMessage(FacesMessage.SEVERITY_WARN, ivs[0].getMessage());
                         FacesContext.getCurrentInstance().addMessage(property, message);
                      }



                      The best strategy here is to create an interceptor as a stereotype and then annotate your action method. That separates the concerns. It would look something like this when you are done.


                      @ValidateModel("contextVariableName")
                      public String actionMethod() {
                      ...
                      }



                      I guess the annotation could take an array of context variable names.

                      • 8. Re: Problem with Validator
                        dan.j.allen

                        Note: You can use Seam's FacesMessages to add the message rather than FacesContext.getCurrentInstance.addMessage().

                        • 9. Re: Problem with Validator
                          sannegrinovero

                          Hi Dan,
                          I like your idea of the Interceptor, really good.
                          What is your opinion about the proper way of stopping rendering?
                          from the interceptor I could skip the action method, add the message to context and return null to manage navigation?


                          What about having such an interceptor included in Seam?
                          We currently do it the JSF way but it's ugly and layout dependent :-(, Seam could be used to improve that too?

                          • 10. Re: Problem with Validator
                            dan.j.allen

                            Yes, I think that whole object validation would be something very nice to have in Seam. Below is a very rough cut of some code I threw together. It makes an assumption that the intercepted object is a org.jboss.seam.framework.Home, but it gets the idea through the door. Theoretically, the object to validate could be passed in to the annotation as a context variable. The linkage to Home isn't necessary.


                            Note: This code uses the Seam interceptor API.


                            HomeInterceptor.java
                            @Interceptor(type = InterceptorType.CLIENT, stateless = true)
                            public class HomeInterceptor {
                                @AroundInvoke
                                public Object aroundInvoke(InvocationContext ctx) throws Exception {
                                    if (!ctx.getMethod().isAnnotationPresent(ValidatableAction.class)) {
                                        return ctx.proceed();
                                    }       
                            
                                    // Assume we were applied to a Home object
                                    Object instance = ((Home) ctx.getTarget()).getInstance();
                                    ClassValidator validator = Validators.instance().getValidator(instance);
                                    InvalidValue[] ivs = validator.getInvalidValues(instance);
                                    if (ivs.length > 0)
                                    {       
                                        FacesMessages.instance().add(ivs);
                                        return null;
                                    }       
                                    return ctx.proceed();
                                }   
                            }



                            @Home
                            @Target({ElementType.TYPE})
                            @Retention(RetentionPolicy.RUNTIME)
                            @Documented
                            @Inherited
                            @Interceptors(HomeInterceptor.class)
                            public @interface Home {}



                            @ValidatableAction
                            @Target({ElementType.METHOD})
                            @Retention(RetentionPolicy.RUNTIME)
                            @Documented
                            @Inherited
                            public @interface ValidatableAction {}



                            Here is how it would be used:


                            @Name("facilityHome")
                            @Home
                            public class FacilityHome extends EntityHome<Facility> {
                                @ValidatableAction
                                public String update() {
                                    return super.update();
                                }
                            }



                            One thing to keep in mind is that you are validating the model directly, so if you are working on a managed entity instance, you have to be sure to use manual flushing. The single field validators use a pre-assignment check so the field is not actually set when the validation fails.

                            • 11. Re: Problem with Validator

                              +1 on this.  Dan, I had to implement something very similar to what you describe for a client but a bit more generic.  I would think this is a common requirement and deserves a feature request.

                              • 12. Re: Problem with Validator

                                If you are using ajax4j in your application where you can use this approach to do smart client side cross field validation. With this approach you update all fields to the component tree using ajax, and put a validator on the last field. For code/examples check out this Jsf Seam Validation page.