-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: [Hibernate Validator] how to set language on runtime
PostPosted: Tue Jul 03, 2007 6:49 am 
Newbie

Joined: Wed Jul 26, 2006 7:17 am
Posts: 7
Hi,

i use Hibernate Annotations, Hibernate EntityManager and Hibernate Validator annotations on my data model entity properties to validate min and max length of my persistent entity fields.

Question: Is there a way to change the localization language for Hibernate Validator result messages on runtime to support multi language clients ? (it always returns my messages in the server default locale)

Thanks,

Frobb


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 03, 2007 12:03 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
Interesting, Today the Locale is read at Validator init time. so Locale.setDefault() before initialization will work.

If you have users with different languages, this is not supported out of the box (http://opensource.atlassian.com/project ... owse/HV-31)
So you can:
- use JBoss Seam which use it's own interpolation mechanism
- write a MessageInterpolator that will deal with dynamic locale definitions (should be fairly easy, check DefaultMessageInterpolator)

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 04, 2007 3:23 am 
Newbie

Joined: Wed Jul 26, 2006 7:17 am
Posts: 7
Ok, I see...

Seam is no option for me, but I will try changing the MessageInterpolator. It seems to me a good way to pass "per hibernate session" language into the Interpolator by using a ThreadLocal which holds the current client request lanquage ...

Frobb


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 04, 2007 4:45 am 
Newbie

Joined: Wed Jul 26, 2006 7:17 am
Posts: 7
Hello,

I implemented my version of MessageInterpolation but there is a bug that kills its usefullnes (set by hibernate.validator.message_interpolator_class setting):
The mapped message interpolator is only "instantiated" but never "initialized" like the DefaultMessageInterpolator, so there is no way to replace it with my "copy-and-paste-engineered" Interpolator.
If not initialized there is no information for my code to implement a new useful "replace" method because it has no access to the annotationParameters Map containing the annotation parameters used to replace the parameter. (It can't build it on its own because it gets not initialized, and it can't access it from the DefaultMessageInterpolator because it is private)

Is the only way to provide a new 'useful' interpolator by replacing the class DefaultMessageInterpolator in the jar file or did i miss something?

Frobb


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 04, 2007 6:28 am 
Newbie

Joined: Wed Jul 26, 2006 7:17 am
Posts: 7
Ok, here is my workaround solution without modifying jars or exchanging classes, using reflection to access private fields and lots of wreid class-casting stuff. (To get around these workaround the MessageInterpolator should provide direct access to these fields ?)

Code:
public class TranslatingInterpolator implements MessageInterpolator {
String interpolateMessage = null;
    private DefaultMessageInterpolator interpolator;
    static Map<Locale, ResourceBundle> resourceBundles = new    HashMap<Locale,ResourceBundle>();

private String replace(String message) {
      StringTokenizer tokens = new StringTokenizer( message, "#{}", true );
      StringBuilder buf = new StringBuilder( 30 );
      boolean escaped = false;
      boolean el = false;
      while ( tokens.hasMoreTokens() ) {
         String token = tokens.nextToken();
         if ( !escaped && "#".equals( token ) ) {
            el = true;
         }
         if ( !el && "{".equals( token ) ) {
            escaped = true;
         }
         else if ( escaped && "}".equals( token ) ) {
            escaped = false;
         }
         else if ( !escaped ) {
            if ( "{".equals( token ) ) el = false;
            buf.append( token );
         }
         else {
            Object variable = getAnnotationParameters().get( token );
            if ( variable != null ) {
               buf.append( variable );
            }
            else {
               String string = null;
               try {
                  string = getMessageBundle() != null ? getMessageBundle().getString( token ) : null;
               }
               catch( MissingResourceException e ) {
                  //give a second chance with the default resource bundle
               }
               if (string == null) {
                  try {
                     string = getDefaultMessageBundle().getString( token );
                  }
                  catch( MissingResourceException e) {
                            //return the unchanged string
                            buf.append('{').append(token).append('}');
                  }
               }
               if ( string != null ) buf.append( replace( string ) );
            }
         }
      }
      return buf.toString();
   }

   public String interpolate(String message, Validator validator, MessageInterpolator defaultInterpolator) {
        DefaultMessageInterpolatorAggerator defaultInterpolatorAggregator = (DefaultMessageInterpolatorAggerator) defaultInterpolator;
        try {
            interpolator = (DefaultMessageInterpolator) ((Map)getField(defaultInterpolatorAggregator, "interpolators")).get(validator);
        } catch (Throwable e) {
            throw new IllegalAccessError("Workaround failed!");         
        }


        if ( getAnnotationMessage().equals( message ) ) {
         //short cut
            if (interpolateMessage == null) {
                interpolateMessage = replace( getAnnotationMessage() );
            }
            return interpolateMessage;
      }
      else {
         //TODO keep them in a weak hash map, but this might not even be useful
         return replace( message );
      }
   }

    private ResourceBundle getMessageBundle() {
        Locale locale = RequestContext.getLocale();
        if (resourceBundles.containsKey(locale)) return resourceBundles.get(locale);
        Locale oldDefault = Locale.getDefault();
        ResourceBundle rb;
        try {
            Locale.setDefault(locale);
            rb = ResourceBundle.getBundle("org.hibernate.validator.resources.DefaultValidatorMessages", locale, getClass().getClassLoader());
            resourceBundles.put(locale,rb);
            return rb;
        } catch( Throwable t) {
            return null;
       }finally {
            Locale.setDefault(oldDefault);
       }
    }


    public String getAnnotationMessage() {
        return (String) getField(interpolator,"annotationMessage");
    }

    public Map getAnnotationParameters() {
        return (Map) getField(interpolator,"annotationParameters");
   }
    public ResourceBundle getDefaultMessageBundle() {
        return (ResourceBundle) getField(interpolator,"defaultMessageBundle");
   }

    public Object getField(Object o, String name){
        try {
            Field field = o.getClass().getDeclaredField(name);
            field.setAccessible(true);
            return field.get(o);
        } catch (Throwable e) {
            throw new IllegalAccessError("MessageInterpolator Workaround Failed!");
        }
    }



Thanks for the help, problem solved.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 11, 2007 12:01 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
be careful your code is not thread safe.

I think I would have gone that way:

Code:
Type type = validator.getClass().getGenericInterfaces()[0]; //access Validator<T>
ParameterizedType paramType = (ParameterizedType) type;
Type validatedAnnotation = paramType.getActualTypeArguments()[0];
//read message and parameters from validatedAnnotations just like the default interpolator


and cache the Annotation reading per Validator somewhere

_________________
Emmanuel


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.