简体   繁体   中英

ThreadLocale value getting mixed up in Servlet Filter

I am working on a messy Struts 1 application that makes use of a custom context class to store values throughout the application. Basically it is only used to store session scope variables. I guess the reason that this custom class is used is so that other classes which do not have access to the http session can still get and set the session variables.

Anyways, for the most part this works just fine. The custom context is used throughout the Actions and service classes to share variables with no problem. However, I just discovered that things do not work out so nicely using this custom context inside of an Http Filter! It appears that it randomly will pull the value from a different session. And by session, I actually mean thread, since this custom context uses ThreadLocale to do it's dirty work.

Take a look

package com.zero.alpha.common;

import java.io.Serializable;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;

public final class CustomContext implements Serializable {
    private static final long serialVersionUID = 400312938676062620L;
    private static ThreadLocal<CustomContext> local = new ThreadLocal() {
        protected CustomContext initialValue() {
            return new CustomContext("0", "0", Locale.getDefault());
        }
    };
    private String dscId;
    private String sessionId;
    private Locale locale;
    private Map<String, Serializable> generalArea;

    public CustomContext(String dscId, String sessionId, Locale locale) {
        this.dscId = dscId;
        this.sessionId = sessionId;

        if (locale != null) {
            this.locale = locale;
        } else {
            this.locale = Locale.getDefault();
        }
        this.generalArea = new Hashtable();
    }

    public static CustomContext get() {
        return ((CustomContext) local.get());
    }

    public static void set(CustomContext context) {
        local.set(context);
    }

    public String getDscId() {
        return this.dscId;
    }

    public String getSessionId() {
        return this.sessionId;
    }

    public Locale getLocale() {
        return this.locale;
    }

    public Serializable getGeneralArea(String key) {
        return ((Serializable) this.generalArea.get(key));
    }

    public Serializable putGeneralArea(String key, Serializable value) {
        return ((Serializable) this.generalArea.put(key, value));
    }

    public void clearGeneralArea() {
        this.generalArea.clear();
    }

    public Serializable removeGeneralArea(String key) {
        return ((Serializable) this.generalArea.remove(key));
    }
}

Again, this seems to work just fine and dandy inside every other class other than a filter. Let me show you the filter where it messes up.

package com.zero.alpha.myapp.common.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.zero.alpha.common.CustomContext;
import com.zero.alpha.myapp.utility.CommonConstants;
import com.zero.alpha.myapp.utility.CommonHelpers;
import com.zero.alpha.myapp.UserDomain;


public class LoginFilter implements Filter {

    public LoginFilter() {
    }


    public void init(FilterConfig config) throws ServletException {}


    public void destroy() {}

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

        HttpServletRequest req = (HttpServletRequest) request;

        // Don't use the login filter during a login or logout request
        if (req.getServletPath().equals("/login.do")
            || req.getServletPath().equals("/login-submit.do") 
            || req.getServletPath().equals("/logout.do")) {
            chain.doFilter(request, response);

        } else {
            doFilter(req, (HttpServletResponse) response, chain);
        }
    }

    protected void doFilter(HttpServletRequest request, HttpServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        HttpSession session = request.getSession(false); 

        // This is the problem right here. Sometimes this will grab the value of a different user currently logged in
        UserDomain user = (UserDomain) CustomContext.get()
            .getGeneralArea(CommonConstants.ContextKey.USER_SESSION);

        if (session == null || user == null) {
            // Unauthorized
            response.sendRedirect(loginPage);
        } else {
            // Authorized
            session.setAttribute("userInfo", CommonHelpers.getUserDisplay(user));
            chain.doFilter(request, response);
        } 
    }
}

When the custom context is used to grab the user in the doFilter method, it will randomly grab the user object from another logged in user. Obviously not a good situation!

The only time this happens is after some activity from a different logged in user. I could sit there all day and keep refreshing user A's session and there wouldn't be an issue. However, after taking some action as user B and then refreshing user A's session again, it will usually be swapped. But then if I refresh user A's session again, things are back to normal.

I've noticed this happens extremely more frequently when the application is actually deployed to a remote development tomcat server. It still happens when running locally, but not nearly as often as when deployed remotely. It happens almost 100% of the time remotely.

I have examined the session variable inside of the filter, and there doesn't appear to be an issue with the session itself. I have confirmed that the session id is still correct even when the incorrect user is pulled from the ThreadLocale variable.

Can anyone help me out? Thanks.

Your strategy is irretrievably flawed. Unless your servlet engine is single-threaded, you cannot rely on the same thread to handle every request in a given session. Moreover, and more relevant to the problem stated, even when the same thread to handle one request in a given session is also used to handle the next request in that session, it is not safe to assume that it does not handle a request belonging to a different session in between. There is no guarantee of thread / session binding.

Anyway, you seem to be missing the obvious. If you want to store session-scope variables, then store them in the session . That's what session attributes are for. See HttpSession.setAttribute() and HttpSession.getAttribute() -- you can use these to set and retrieve your context object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM