3 Replies Latest reply on Jul 5, 2006 7:02 PM by David Bernstein

    i18n gotcha suggested by Seam manual

    David Bernstein Newbie

      I had gotten a very simple Seam project created in Eclipse successfully cloned from seam-registration. I started changing it a bit to be a bit more like the seam-booking example (just the registration page part), which included adding the @NotNull and @Length annotations that come from Hibernate. I'm trying to build an application with Seam and Facelets and Hibernate, which seems to be the recommended approach from the documentation.

      Since I am the unusual kind of American who expects that his software will need a non-English UI in order to sell it abroad :-), I had also followed the recommendation of putting a message.properties file in WEB-INF/classes, as suggested in section 5.2 (Internationalization.Labels) of the reference manual. Well, it turns out that if you do that, then you override the Hibernate validator message properties from its DefaultValidatorMessages properties. This caused me to get:


      2006-07-04 15:16:30,921 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[localhost].[/seam-registration].[Faces Servlet]] Servlet.service() for servlet Faces Servlet threw exception
      java.lang.IllegalArgumentException: could not instantiate ClassValidator
      at org.hibernate.validator.ClassValidator.createValidator(ClassValidator.java:265)
      at org.hibernate.validator.ClassValidator.createMemberValidator(ClassValidator.java:233)
      at org.hibernate.validator.ClassValidator.initValidator(ClassValidator.java:176)
      at org.hibernate.validator.ClassValidator.<init>(ClassValidator.java:110)
      at org.hibernate.validator.ClassValidator.<init>(ClassValidator.java:98)
      at org.hibernate.validator.ClassValidator.<init>(ClassValidator.java:92)
      at org.jboss.seam.Component.getValidator(Component.java:652)
      at org.jboss.seam.util.Validation.getValidator(Validation.java:23)
      at org.jboss.seam.ui.ModelValidator.validate(ModelValidator.java:38)
      at javax.faces.component._ComponentUtils.callValidators(_ComponentUtils.java:133)
      at javax.faces.component.UIInput.validateValue(UIInput.java:254)
      at javax.faces.component.UIInput.validate(UIInput.java:269)
      at javax.faces.component.UIInput.processValidators(UIInput.java:144)
      at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:417)
      at javax.faces.component.UIForm.processValidators(UIForm.java:68)
      at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:417)
      at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:142)
      at org.apache.myfaces.lifecycle.LifecycleImpl.processValidations(LifecycleImpl.java:240)
      at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:76)
      at javax.faces.webapp.FacesServlet.service(FacesServlet.java:106)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
      at org.jboss.seam.servlet.SeamRedirectFilter.doFilter(SeamRedirectFilter.java:30)
      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)
      at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
      at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
      at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:175)
      at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:74)
      at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
      at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
      at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
      at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
      at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:869)
      at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:664)
      at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527)
      at org.apache.tomcat.util.net.MasterSlaveWorkerThread.run(MasterSlaveWorkerThread.java:112)
      at java.lang.Thread.run(Thread.java:595)
      Caused by: java.util.MissingResourceException: Can't find resource for bundle java.util.PropertyResourceBundle, key validator.notNull
      at java.util.ResourceBundle.getObject(ResourceBundle.java:325)
      at java.util.ResourceBundle.getString(ResourceBundle.java:285)
      at org.hibernate.validator.ClassValidator.replace(ClassValidator.java:518)
      at org.hibernate.validator.ClassValidator.createValidator(ClassValidator.java:260)
      ... 40 more


      I was quite perplexed for a few hours. I'm sure that as I become more familiar with Seam this sort of time will shorten, but I do think that it's one of those issues that can discourage people from using Seam. Having the source for Seam and Hibernate around certainly helped in figuring out a diagnosis.

      Rooting around in the examples, I noticed that the messages.properties files for the dvdstore and issues examples define various "validator.*" properties:

      validator.assertFalse=assertion failed
      validator.assertTrue=assertion failed
      validator.future=must be a future date
      validator.length=length must be between {min} and {max}
      validator.max=must be less than or equal to {value}
      validator.min=must be greater than or equal to {value}
      validator.notNull=may not be null
      validator.past=must be a past date
      validator.pattern=must match "{regex}"
      validator.range=must be between {min} and {max}
      validator.size=size must be between {min} and {max}
      validator.email=not a well-formed email address


      Removing or renaming my messages.properties file makes the problem go away.

      So, first, a caveat emptor warning to others so that you can avoid my misfortune.

      And, second, a couple of suggestions for Seam:

      * at the least, I think there ought to be some warning about this in the reference manual (easy)

      * I think that there ought to be a way to get the usefulness of a messages.properties file default (i.e. not having to put <f:loadBundle ... /> in every page) without incurring the burden of having to redefine the various Hibernate validator text properties: there's a maintenance and correctness burden (less easy)

      Best regards,
      Dave


        • 1. Re: i18n gotcha suggested by Seam manual
          David Bernstein Newbie

          Some more issues in internationalization for the unwary uninitiated: It's common in Java to use the period character to separate words in resource property names, e.g.:

          repeat.password=Repeat Password


          Unfortunately, this gets interpreted (by JSF, I think) as a series of properties, so it looks for the "repeat" property of the messages bundle when you use it like:

          <h:outputLabel for="verify" value="#{messages.repeat.password}"/>



          11:39:44,796 ERROR [STDERR] Jul 5, 2006 11:39:44 AM com.sun.facelets.FaceletViewHandler handleRenderException
          SEVERE: Error Rendering View[/register.xhtml]
          javax.faces.el.PropertyNotFoundException: /register.xhtml @42,92 value="#{messages.repeat.password}": Bean: java.lang.String, property: password
          at com.sun.facelets.el.LegacyValueBinding.getValue(LegacyValueBinding.java:58)
          at javax.faces.component.UIOutput.getValue(UIOutput.java:75)
          at org.apache.myfaces.renderkit.RendererUtils.getStringValue(RendererUtils.java:225)
          ...


          Trying to change the period to a dash doesn't work either (it gets interpreted as subtraction):

          repeat-password=Repeat Password


          <h:outputLabel for="verify" value="#{messages.repeat-password}"/>



          11:45:28,968 ERROR [STDERR] Jul 5, 2006 11:45:28 AM com.sun.facelets.FaceletView
          Handler handleRenderException
          SEVERE: Error Rendering View[/register.xhtml]
          java.lang.NumberFormatException: For input string: "repeat"
          at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1224)
          at java.lang.Double.valueOf(Double.java:447)
          at java.lang.Double.<init>(Double.java:539)
          at com.sun.el.lang.ELArithmetic$DoubleDelegate.coerce(ELArithmetic.java:137)
          at com.sun.el.lang.ELArithmetic.coerce(ELArithmetic.java:362)
          at com.sun.el.lang.ELArithmetic.subtract(ELArithmetic.java:285)
          at com.sun.el.parser.AstMinus.getValue(AstMinus.java:44)
          at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:192)
          at com.sun.facelets.el.TagValueExpression.getValue(TagValueExpression.java:71)
          at com.sun.facelets.el.LegacyValueBinding.getValue(LegacyValueBinding.java:56)
          at javax.faces.component.UIOutput.getValue(UIOutput.java:75)
          at org.apache.myfaces.renderkit.RendererUtils.getStringValue(RendererUtils.java:225)
          at org.apache.myfaces.renderkit.html.HtmlLabelRenderer.encodeBegin(HtmlLabelRenderer.java:105)
          at javax.faces.component.UIComponentBase.encodeBegin(UIComponentBase.java:307)
          at com.sun.facelets.tag.jsf.ComponentSupport.encodeRecursive(ComponentSupport.java:232)
          at com.sun.facelets.tag.jsf.ComponentSupport.encodeRecursive(ComponentSupport.java:239)
          at com.sun.facelets.tag.jsf.ComponentSupport.encodeRecursive(ComponentSupport.java:239)
          at com.sun.facelets.tag.jsf.ComponentSupport.encodeRecursive(ComponentSupport.java:239)
          at com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:554)
          at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:352)
          at javax.faces.webapp.FacesServlet.service(FacesServlet.java:107)
          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
          ...


          Camel-casing, e.g.

          repeatPassword=Repeat Password


          <h:outputLabel for="verify" value="#{messages.repeatPassword}"/>


          does work as a workaround, as one might expect.

          • 2. Re: i18n gotcha suggested by Seam manual
            Pete Muir Master

            Try the alternative syntax:

            <h:outputText value="#{messages['repeat.password']} />
            <h:outputText value="#{messages['repeat-password']} />
            


            I agree, the dot notation is most useful as it allows you 'package' messages e.g. customer.name, button.ok.



            • 3. Re: i18n gotcha suggested by Seam manual
              David Bernstein Newbie

              Thank you for pointing out that workaround.