Question

I am using a ThreadLocal object which stores a String. I am setting the String value to the ThreadLocal object in a filter, that intercepts all requests subject to certain conditions. Also I am setting the ThreadLocal's string value to the HttpSession as an attribute.

The attribute in the session is used in multiple jsps, and eventually passed on to the business layer.

The problem I am facing is that, multiple requests originating from different Clients, at some point of time get the same string value, though the session is different.

So my understanding is that, multiple sessions access the same thread. I don't see any other explanation for this.

Setting the attribute to the request causes problems when moving between jsps. As there are redirects by spring security, which means that request attribute is lost.

So is there any way I can change the implementation such that multiple sessions don't use the same thread ?

Edit : Adding sample code

public class ContextFilter implements Filter {

    //No significant variables other than constants

    public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {

            // Set the Session Timeout Object
            SessionTimeoutObject obj = (SessionTimeoutObject) httpRequest
                    .getSession().getAttribute(KEY);
            if (obj == null) {
                httpRequest.getSession().setAttribute(KEY,
                        new SessionTimeoutObject());
            }

            if( some conditions ) {
                chain.doFilter(request, response);
            } else {
                //Defaulting identifier
                String identifier = "init";

                if (ContextHolder.getId() != null
                        && !ContextHolder.getId().equals("")) {
                    identifier = ContextHolder.getId());
                }

                //Do some thing 

                //Compare the identifier with the value in session and use if it exists
                String existingId = (String) httpRequest.getSession()
                        .getAttribute(ID_KEY);
                if (existingId != null
                        && !existingId.trim().equals("")) {
                    identifier = existingId;
                }

                //Setting id to context only happens here
                ContextHolder.setId(identifier);
                //Validate the identifier

                //Get Business Obj method using identifier
                BusinessObj bo = getBusObj(identifier);

                //everything above is successful so set in session
                httpRequest.getSession().setAttribute("identifier", identifier);
                httpRequest.getSession().setAttribute("BusinessObj",
                    bo);

                //no exceptions yet then good to go
                chain.doFilter(request, response);
            }

    }


}

public class SessionTimeoutObject implements Serializable, HttpSessionBindingListener {

    private String backup;

    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        //Mainly for debuggin what happens to the session
        backup = (String) event.getSession().getAttribute("identifier");
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        //Mainly for debuggin what happens to the session
        if (ContextHolder.getId() != null) {
            backup = ContextHolder.getId();
        } 
    }

}

class ContextHolder {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    public ContextHolder() {
    }

    public static void setId(String identifier) {
        if (null == identifier) {
            //throw some exception
        }
        contextHolder.set(identifier);
    }

    public static String getId() {
        return (String) contextHolder.get();
    }

    public static void clearId() {
        contextHolder.remove();
    }

    public static void setDefaultId() {
        ContextHolder.clearId();
        contextHolder.set('init');
    }
}
Was it helpful?

Solution

You should wrap your code in a try/finally block, in the finally block do a ContextHolder.clearId() that clears the context holder. This is important as request handling threads are reused and which means that the ContextHolders thread local retains the same id as before. – M. Deinum

Doing the above solved the issue.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top