8 Replies Latest reply on Aug 13, 2007 7:30 PM by Matt Drees

    How to display exception messages for fields

    Ning Zhao Apprentice

      Here is the entity class:


      @Entity
      @Name("foo")
      @Table(name = "foo")
      public class Foo implements Serializable {
      ...
      @Column(name = "non_repeat_cycle", nullable = false)
       public Short getNonRepeatCycle() {
       return nonRepeatCycle;
       }
      
      public void setNonRepeatCycle(Short nonRepeatCycle)
       throws IllegalNonRepeatCycleException {
       boolean valid = true;
       if (nonRepeatCycle <= 0) {
       valid = false;
       throw new IllegalNonRepeatCycleException(nonRepeatCycle);
       }
       if (valid){
       this.nonRepeatCycle = nonRepeatCycle;
       }
       }
      ...
      



      Client code in xhtml:
      <s:decorate template="layout/edit.xhtml">
       <ui:define name="label">Non-repeat cycle:</ui:define>
       <h:inputText
       value="#{fooManager.foo.nonRepeatCycle}" required="true" />
       <h:outputText value=" days" />
       </s:decorate>
      
      


      The idea is that, this way the entity knows how to validate itself and I do not have to write a separate validator.

      In fact it works very well, when I tested with the number "-3", there is error message displayed in red and the conversation hanged like expected (hanged but not killed, after I corrected the value, the conversation goes smoothly), and the view is also like expected....The only problem is, the error message looks like this:
      /foo-management.xhtml @58,31 value="#{fooManager.foo.nonRepeatCycle}": Error writing 'nonRepeatCycle' on type myproject.entity.Foo
      


      My exception class looks like this:
      public class IllegalNonRepeatCycleException extends Exception {
       private static final String DEFAULT_MSG = "Non-repeat-cycle must be greater than zero!";
       @Logger
       private static Log log;
      
       public IllegalNonRepeatCycleException(){
       super(DEFAULT_MSG);
       }
      
       public IllegalNonRepeatCycleException(Short nonRepeatCycle) {
       super(DEFAULT_MSG + " " + nonRepeatCycle + " is illegal.");
       log.info("Exception generated!");
       }
      
       public String toString(){
       return this.getMessage();
       }
      }
      


      It would be nice if the error message in the view can be the exception message. How to do this? Thanks in advance for any help!


      Regards,
      Ellen

        • 1. Re: How to display exception messages for fields
          Pete Muir Master

          Why not use hibernate validators?

          • 2. Re: How to display exception messages for fields
            Ning Zhao Apprentice

             

            "pete.muir@jboss.org" wrote:
            Why not use hibernate validators?


            1. There is less stuff to configure if I integrate the "validator" into the entity itself

            2. This approach works really well with Seam when the entity travels between layers/tiers. The setter is the _only_ place where validation code is needed. In the conversation bean, I call setters of entities anyway....

            3. In fact the validity of the fields belongs to business logic. The entity class is much more reusable if this business logic is contained in itself. Otherwise, the validity logic is contained in the hibernate validator, the hibernate validator dependency sneaks in....

            4. The exception classes becomes a central error repository. With this approach, the view code only needs to react to POJO business exceptions....And in the message bundle, I can write localized error messages according to the type of exceptions. I prefer the concept of a business exception (which is caused by incorrect user input and recovable) to the concept of "validation exception".



            • 3. Re: How to display exception messages for fields
              Ning Zhao Apprentice

              A problem occurs when there is constraint with date/time type fields. If there is a field called "startDate" and the setter checks if it is later than today. It will be fine for user input but when I want to display an old record, data loading will fail. A work around is that to store a timestamp field, let me call it "initDate", then the setter can always check if the "startDate" is later than the "initDate".

              • 4. Re: How to display exception messages for fields
                Pete Muir Master

                I'm not sure its a good idea, not least because you are going to need to write a custom exception handling layer that correctly deals with transaction management.

                • 5. Re: How to display exception messages for fields
                  Ning Zhao Apprentice

                  Sorry, the workaround does not really work, I got this exception just now:

                  ...
                  Caused by: org.hibernate.PropertyAccessException: Exception occurred inside setter of myproject.entity.Foo.startDate
                   at org.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:65)
                   at org.hibernate.tuple.entity.AbstractEntityTuplizer.setPropertyValues(AbstractEntityTuplizer.java:337)
                   at org.hibernate.tuple.entity.PojoEntityTuplizer.setPropertyValues(PojoEntityTuplizer.java:200)
                   at org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:3566)
                   at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:129)
                   at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:854)
                   at org.hibernate.loader.Loader.doQuery(Loader.java:729)
                   at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
                   at org.hibernate.loader.Loader.loadCollection(Loader.java:1994)
                   at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:36)
                   at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:565)
                   at org.hibernate.event.def.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:60)
                   at org.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:1716)
                   at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:344)
                   at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
                   at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:109)
                   at org.hibernate.collection.PersistentBag.size(PersistentBag.java:225)
                   ......
                   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.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:112)
                   at org.jboss.ejb3.interceptor.InvocationContextImpl.proceed(InvocationContextImpl.java:166)
                   at org.jboss.seam.intercept.EJBInvocationContext.proceed(EJBInvocationContext.java:44)
                   at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:56)
                   at org.jboss.seam.core.BijectionInterceptor.aroundInvoke(BijectionInterceptor.java:46)
                   at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                   at org.jboss.seam.persistence.ManagedEntityIdentityInterceptor.aroundInvoke(ManagedEntityIdentityInterceptor.java:45)
                   at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                   at org.jboss.seam.transaction.RollbackInterceptor.aroundInvoke(RollbackInterceptor.java:31)
                   at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                   at org.jboss.seam.core.ConversationInterceptor.aroundInvoke(ConversationInterceptor.java:63)
                   at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                   at org.jboss.seam.core.MethodContextInterceptor.aroundInvoke(MethodContextInterceptor.java:42)
                   at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                   at org.jboss.seam.intercept.RootInterceptor.invoke(RootInterceptor.java:106)
                   at org.jboss.seam.intercept.SessionBeanInterceptor.aroundInvoke(SessionBeanInterceptor.java:53)
                   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.ejb3.interceptor.InvocationContextImpl.proceed(InvocationContextImpl.java:118)
                   at org.jboss.ejb3.interceptor.EJB3InterceptorsInterceptor.invoke(EJB3InterceptorsInterceptor.java:63)
                   at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
                   at org.jboss.ejb3.entity.ExtendedPersistenceContextPropagationInterceptor.invoke(ExtendedPersistenceContextPropagationInterceptor.java:57)
                   at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
                   at org.jboss.ejb3.entity.TransactionScopedEntityManagerInterceptor.invoke(TransactionScopedEntityManagerInterceptor.java:54)
                   at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
                   at org.jboss.ejb3.AllowedOperationsInterceptor.invoke(AllowedOperationsInterceptor.java:47)
                   at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
                   at org.jboss.aspects.tx.TxPolicy.invokeInCallerTx(TxPolicy.java:126)
                   ... 86 more
                  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.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:42)
                   ... 138 more
                  Caused by: java.lang.NullPointerException
                   at java.util.Date.getMillisOf(Date.java:938)
                   at java.util.Date.before(Date.java:897)
                   at myproject.entity.Foo.setStartDate(Foo.java:239)
                   ... 143 more
                  
                  
                  


                  • 6. Re: How to display exception messages for fields
                    Ning Zhao Apprentice

                     

                    "pete.muir@jboss.org" wrote:
                    I'm not sure its a good idea, not least because you are going to need to write a custom exception handling layer that correctly deals with transaction management.


                    Yes you are right.

                    Another problem (which might be serious to some kind of applications) is that it might degrade performance since it checks the validity against the data from the database as well.

                    • 7. Re: How to display exception messages for fields
                      Ning Zhao Apprentice

                       

                      "pete.muir@jboss.org" wrote:
                      I'm not sure its a good idea, not least because you are going to need to write a custom exception handling layer that correctly deals with transaction management.


                      I wrote that in my conversation beans. Usually when something bad happens due to transaction management, the end users can not do anything with it, it has to be fixed by the programmers. So I simply show the user a "Sorry, this error is logged and will be inspected by our programmer very soon." page.

                      • 8. Re: How to display exception messages for fields
                        Matt Drees Master

                        As far as keeping validation logic in the entity itself, you can do something like this:

                        @Entity
                        @Name("foo")
                        @Table(name = "foo")
                        public class Foo implements Serializable {
                        ...
                         @AssertTrue(message = "{invalid.nonRepeatCycle.message}")
                         public boolean isNonRepeatCycleValid() {
                         return nonRepeatCycle > 0;
                         }
                        ...
                        }
                        


                        It seems to me you're better of using Hibernate's validation framework rather than creating your own; I imagine you'd end up duplicating a lot of it.