How to set Locale in Bean Validation

十年热恋 提交于 2019-12-03 16:17:58

I guess it comes down to what you actually mean with

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

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).

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.

Analog to @Hardy's answer you can also store the Local in the resource SessionContext data map for this purpose and retrieve it in your MessageInterpolator implementation.

In your remote bean methods you can pass the client locale as an argument and set it on method entry. A possible setup could look like this:

Locale retrieval interface

interface ILocale
{
    public Locale getLocale();
}

LocaleMessageInterpolator

Extend your default MessageInterpolator. Had to use interface to obtain locale to make it usable outside of EJB application.

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

Application scoped bean

Register the new MessageInterpolator in your validator factory. If all other Beans inject AppBean the constant could be private and a setClientLocale() method could be provided.

@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("javax.persistence.validation.factory", validatorFactory); }

Remote Bean

Save current client locale in 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
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!