Search code examples
javalocaleguicethread-local

ThreadLocal get() gives unexpected result when set() method is not invoked


I have base class which contains ThreadLocal :

@Singleton
public class BaseView extends HttpServlet {

protected ThreadLocal<Locale> locale = new ThreadLocal<Locale>();

private Locale getLocale() {
    return (Locale) ObjectUtils.defaultIfNull(locale.get(), Locale.ENGLISH);
}

...

}

And it is extended in EmailValidatedView:

@Singleton
public class EmailValidatedView extends BaseView {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String token = req.getParameter("token");
    if (token != null) {
        try {
            User user = userService.validateEmail(token);
            locale.set(user.parseLocale());
        } catch (ServiceException e) {
            e.printStackTrace();
        }
    }
    sendResponse("validatedEmail.vm", resp.getWriter(), $());
}
}

When token is invalid, I get ServiceException and locale is not set. In this case sendResponse() method should use default locale - English. However, if I make refresh for the same page in browser with invalid token, I may get different/non related languages every time. Why does it happen?


Solution

  • Most HTTP servers reuse threads from a pool. Keeping a thread-safe config is a good thing, and ThreadLocal can help, but your logic is structured such that your locale will not be reset every request. Therefore, it's unsurprising that the thread's old locales from past requests will bleed through.

    You'll need to ensure your locale is set every single request that reads it, and that the default locale is set (or equivalently that the ThreadLocal value is cleared) for every new request.