如何在Bean验证中设置语言环境

如何在Bean验证中设置语言环境

问题描述:

默认情况下,Bean验证基于整个JVM通用的Locale.getDefault()获取区域设置.

By default Bean Validation gets Locale based on Locale.getDefault(), which is common to whole JVM.

如何更改当前EJB方法调用的BeanValidation的语言环境?

How to change BeanValidation's Locale for current EJB method call?

我正在使用JavaEE7,希望从JPA和Bean验证的集成中受益,即对插入/更新/删除事件的自动触发验证,并尽可能避免手动编写所有内容.

I'm using JavaEE7 and want to get benefits from integration of JPA and Bean Validation, i.e. automatic triggering validation on insert/update/delete events, and as far as possible avoid of writing everything manually.

编辑

毕竟,我只是从EJB返回未插入的消息:

After all, I'm just returning non-interpolated messages from EJB:

public class DoNothingMessageInterpolator implements MessageInterpolator {
    @Override
    public String interpolate(String message, Context context) {
        return message;
    }
    @Override
    public String interpolate(String message, Context context, Locale locale) {
        return message;
    }
}

,然后在Web层中进行插值:

and later interpolating them in Web tier:

try{
    //invoke ejb
}catch( EJBException ejbex ){
    if( ejbex.getCause() instanceof ConstraintViolationException ){
        ConstraintViolationException cve = (ConstraintViolationException) ejbex.getCause();
        WebUtils.printConstraintViolationMessages("NewConferenceForm:", context, cve, new Locale(languageCtrl.getLocaleCode()) );
        return null;
    }else throw ejbex;
}catch( Exception e ){
        context.addMessage(null, new FacesMessage( FacesMessage.SEVERITY_ERROR, "Oops.", ""));
        return null;
}


public class WebUtils {

    public static void printConstraintViolationMessages(
        String formPrependId, 
        FacesContext context, 
        ConstraintViolationException cve,
        Locale locale )
    {
        Iterator<ConstraintViolation<?>> iter = cve.getConstraintViolations().iterator();
        while( iter.hasNext() ){
            final ConstraintViolation<?> cv = iter.next();

            javax.validation.MessageInterpolator.Context c = new javax.validation.MessageInterpolator.Context()
            {
                @Override public <T> T unwrap(Class<T> type) {
                    try {
                        return type.newInstance();
                    } catch (InstantiationException ex) {
                        Logger.getLogger(ConferencesCtrl.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (IllegalAccessException ex) {
                        Logger.getLogger(ConferencesCtrl.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    return null;
                }
                @Override
                public ConstraintDescriptor<?> getConstraintDescriptor() {
                    return cv.getConstraintDescriptor();
                }
                @Override
                public Object getValidatedValue() {
                    return cv.getInvalidValue();
                }
            };

            ResourceBundleMessageInterpolator rbmi = new ResourceBundleMessageInterpolator();
            String interpolatedMsg = rbmi.interpolate(cv.getMessage(), c, locale );

            //TODO: check if clientId exists
            context.addMessage( formPrependId+cv.getPropertyPath().toString(), new FacesMessage( interpolatedMsg ) );
        }
    }

}

我想这取决于您对

如何更改当前EJB方法调用的BeanValidation的语言环境?

How to change BeanValidation's Locale for current EJB method call?

例如,假设每个呼叫都是由给定的用户进行的,并且该用户具有关联的 Locale ,则需要一个自定义的 MessageInterpolator .您可以通过 validation.xml 配置自定义实现(请参见

Assuming for example that each call is made by a given user and this user has an associated Locale, you would need a custom MessageInterpolator. You would configure your custom implementation via validation.xml (see example in online docs).

实施明智,您可以让繁重的工作由代表团来完成.一旦确定了 Locale ,您的自定义消息插值器就可以实例化默认的Hibernate Validator ResourceBundleMessageInterpolator 并将插值调用委托给它.后者可以通过 ThreadLocaL 实现. EJB方法将在 ThreadLocal 中将用户设置为Local,而您的自定义消息插值器将从此处将其拾取.

Implementation wise you can let the heavy lifting be done by delegation. Your custom message interpolator could instantiate the default Hibernate Validator ResourceBundleMessageInterpolator and delegate the interpolation calls to it, once the Locale is determined. The latter can be achieved by a ThreadLocaL. The EJB method would set the users Local in a ThreadLocal and your custom message interpolator would pick it up from there.