xmartinez
xmartinez

Reputation: 65

jsf 2.2 + Spring 4 JSR-349 using Spring validator with JSF Locale

I have an application using JSF 2.2 (with primefaces) in the front layer and Spring 4 on the business layer.

I am using Tomcat 7.

I am not using Spring MVC. Presentation Layer is pure JSF and I access Spring beans using @ManagedProperty(#{someSpringBean})

I am using JSR-349 for client and server side validation. I have my ValidationMessages.properties on the classpath.

On client side validation there is no problem because Primefaces manages everything.

On Facade layer (server side method validation) I need Spring to interpolate validation messages using JSF current locale (FacesContext.getCurrentInstance().getViewRoot().getLocale()).

It's worth saying that the users of my application are allowed to change locale through menu option.

So, how can I make Spring use JSF locale when interpolating messages in server side validation?

I'll give you some code so you can understand better my needs:

JSF Bean

@ManagedBean
@ViewScoped
public class RegisterBean {

    @ManagedProperty(value = "#{partnerServiceFacade}")
    private PartnerServiceFacade partnerServiceFacade;

    // properties...    

    public String registerPartner() {
        ...
        partner = partnerServiceFacade.registerPartner(partner);
    }    
}

Facade interface:

@Validated    
public interface PartnerServiceFacade {

    @PreAuthorize("hasRole('ROLE_USER')")
    public Partner registerPartner(@Valid Partner toRegister);

    //Other methods
}

JPA Entity:

@Entity
@Table(name = DBConstants.TABLE_PARTNER)
@Inheritance(strategy = InheritanceType.JOINED)
public class Partner extends XWeedDBEntity {

    private static final long serialVersionUID = 5692151244956513381L;

    @Id
    @Column(name = DBConstants.PARTNER_COL_PARTNER_NUMBER)
    private Integer partnerNumber;

    @NotEmpty
    @Column(name = DBConstants.PARTNER_COL_NAME, nullable = false)
    private String name;

    @NotEmpty
    @Column(name = DBConstants.PARTNER_COL_SURNAME, nullable = false)
    private String surname;

    @NotEmpty
    @Column(name = DBConstants.PARTNER_COL_LASTNAME)
    private String lastname;
}

Spring configuration:

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="defaultEncoding" value="UTF-8"/>
    <property name="basename" value="ValidationMessages" />
</bean>

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="validationMessageSource" ref="messageSource" />
</bean>

Thanks in advance!


I found the solution. Here it is:

Spring validation configuration:

<!-- SPRING MESSAGE LOCATOR -->

<bean id="messageLocator" class="org.springframework.validation.beanvalidation.MessageSourceResourceBundleLocator">
    <constructor-arg index="0" ref="messageSource" />
</bean>

<!-- SPRING LOCALE MESSAGE INTERPOLATOR -->

<bean id="messageInterpolator" class="org.springframework.validation.beanvalidation.LocaleContextMessageInterpolator">
    <constructor-arg index="0">
        <bean id="hibernateMessageInterpolator" class="org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator">
            <constructor-arg name="userResourceBundleLocator" ref="messageLocator" />
            <constructor-arg name="cacheMessages" value="true"/>
        </bean>
    </constructor-arg>
</bean>

<!-- SPRING VALIDATOR -->

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="messageInterpolator" ref="messageInterpolator" />
</bean>

<!-- SPRING ANNOTATION VALIDATION CONFIGURATION -->

<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
    <property name="validator" ref="validator"/>
</bean>

Phase Listener to manage Locale:

public class LocalePhaseListener implements PhaseListener {

// Constants --------------------------------------------------------------------------------------------------------------

private static final long serialVersionUID = 3678092859009088388L;

// Overridden methods -----------------------------------------------------------------------------------------------------

@Override
public void afterPhase(PhaseEvent event) {
}

@Override
public void beforePhase(PhaseEvent event) {
    HttpSession session = (HttpSession) event.getFacesContext().getExternalContext().getSession(false);
    Locale locale = ((UserBean) session.getAttribute("userBean")).getLocale();
    LocaleContextHolder.resetLocaleContext();
    LocaleContextHolder.setLocale(locale);
}

@Override
public PhaseId getPhaseId() {
    return PhaseId.INVOKE_APPLICATION;
}

}

Using LocaleContextMessageInterpolator you enforce Spring to use the locale in LocaleContextHolder, and in the PhaseListener you fill LocaleContextHolder locale with JSF locale.

Upvotes: 2

Views: 1339

Answers (1)

Hardy
Hardy

Reputation: 19119

Provided that you have access to the Locale via some sort of ThreadLocal (not sure whether LocalContextHolder will work, since I am not so familiar with the Spring API), you can jsut provide a custom MessageInterpolator and configure it via validation.xml. See also http://docs.jboss.org/hibernate/validator/5.1/reference/en-US/html_single/#section-validator-factory-message-interpolator.

In the implementation you need to retrieve the current user's Locale and then call the appropriate interpolation method.

Upvotes: 1

Related Questions