如何在Bean验证中设置区域设置

时间:2014-04-15 13:31:43

标签: bean-validation java-ee-7

默认情况下,Bean Validation会根据Locale.getDefault()获取Locale,这对整个JVM来说都是通用的。

如何更改BeanValidation的当前EJB方法调用的Locale?

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

修改

毕竟,我只是从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层中插入它们:

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 ) );
        }
    }

}

2 个答案:

答案 0 :(得分:3)

我想这取决于你的实际含义

  

如何更改BeanValidation的当前EJB方法调用的Locale?

假设例如每个调用都是由给定用户进行的,并且该用户具有关联的 Locale ,则需要自定义 MessageInterpolator 。您可以通过 validation.xml 配置自定义实现(请参阅online docs中的示例)。

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

答案 1 :(得分:1)

根据@ Hardy的回答,您还可以将Local存储在资源SessionContext数据地图中,并在MessageInterpolator实施中检索它。

在远程bean方法中,您可以将客户端语言环境作为参数传递,并将其设置在方法条目上。可能的设置可能如下所示:

区域设置检索界面

interface ILocale
{
    public Locale getLocale();
}

<强> LocaleMessageInterpolator

扩展默认的MessageInterpolator。不得不使用接口来获取区域设置,使其在EJB应用程序之外可用。

public class LocaleMessageInterpolator extends ResourceBundleMessageInterpolator
{
    private final ILocale iLocale;

    public LocaleMessageInterpolator(final ILocale iLocale)
    {
        this.iLocale = iLocale;
    }

    @Override
    public String interpolate(final String messageTemplate, final Context context)
    {
        final Locale locale = this.iLocale == null ? null : this.iLocale.getLocale();

        if (locale == null)
            return super.interpolate(messageTemplate, context);
        else
            return this.interpolate(messageTemplate, context, locale);
    }
}

应用程序范围的bean

在验证工厂注册新的MessageInterpolator。如果所有其他Beans注入AppBean常量可以是私有的,则可以提供setClientLocale()方法。

@Startup
@Singleton
public class AppBean
{
    public static final String CONTEXT_CLIENT_LOCALE_KEY =  "CLIENT_LOCALE_KEY";

    @Resource
    private SessionContext ctx;

    @PostConstruct
    public void init()
    {
        // retrieve client locale from context using anyonymous implementation
        final ILocale ilocale = () -> {
            if (AppBean.this.ctx.getContextData().containsKey(AppBean.CONTEXT_CLIENT_LOCALE_KEY))
                return (Locale) AppBean.this.ctx.getContextData()
                .get(AppBean.CONTEXT_CLIENT_LOCALE_KEY);
            return null;
        };

        // create client locale aware message interpolator
        final LocaleMessageInterpolator localeMessageInterpolator= new LocaleMessageInterpolator(ilocale);

        // configurate validator factory
        ValidatorFactory validatorFactory = Validation.byDefaultProvider().configure().messageInterpolator(localeMessageInterpolator).buildValidatorFactory();

        // register validator factory

configuration.getProperties()。put(&#34; javax.persistence.validation.factory&#34;,validatorFactory);         }

远程Bean

将当前客户端区域设置保存在SessionContext

@Stateless(mappedName = "MyBean")
@Remote(MyBeanRemote.class)
public class MyBean
{
    @Resource
    private SessionContext ctx;

    @Override
    public void create(Locale locale, Foo foo)
    {
        this.ctx.getContextData().put(AppBean.CONTEXT_CLIENT_LOCALE_KEY, locale);
        // persist new Foo
        // thrown validation exceptions are localized
    }
}