Multiple sessions for one servlet in Java

I have one servlet that takes care of multiple sites and so I want to have different sessions for different sites, even if it's the same user.

Is there any support for this in Java or do I need to use attribute names instead? I think the prefix is ​​not a good idea.

/ Br Johannes

+2


source to share


4 answers


This CANNOT be done in a Servlet container based on URL parameters alone; you will have to do it yourself. However, instead of using attribute prefixes in your servlet, the simplest way to manage "separate" sessions is through a filter:

  • Write a simple wrapper class for HttpSession. Does it store an attribute map and return all attributes / values ​​on the specified map; delegate all other methods to the actual session you are certifying. Override the method invalidate()

    to remove the session wrapper instead of killing the entire "real" session.
  • Write a servlet filter; match it to intercept all applicable urls.
  • Maintaining a collection of your session wrappers as an attribute in a real session.
  • In your filter method, retrieve the doFilter()

    appropriate session wrapper from the collection and inject it into the request you chained, wrapping the original request in an HttpServletRequestWrapper, the getSession () method will be overwritten.
  • Your servlets / JSP / etc ... will use "separate" sessions.


Note that "lastAccessedTime" sessions are shared with this approach. If you need to keep these sections, you will have to write your own code to support this setting and to expire the session wrappers.

+1


source


I think you are looking for something like Apache Tomcat . It will manage separate sessions for individual servlet applications.



0


source


A session is unique to a combination of a custom and web application. You can of course deploy your servlet to multiple web applications on the same Tomcat instance, but you won't be able to redirect an HTTP request to different web applications simply based on the URL parameters unless you evaluate the URL parameters in the second servlet and do not redirect the browser for a new url for a specific web application.

Different servlet containers or J2EE application servers may have different options for routing requests to specific web applications, but AFAIK out of the box, Tomcat can only delegate a request based on the hostname or base directory, for example:

0


source


I recently ran into this problem and I went with ChssPly76's suggestion to solve it. I thought I'd post my results here to provide a reference implementation. This has not been thoroughly tested, so please let me know if you notice any flaws.

I am assuming that each servlet request contains a parameter named uiid which is the user id. The requester must track the submission of a new ID every time a link is clicked that opens a new window. In my case, this is enough, but feel free to use any other (possibly safer) method here. Also, I'm working with Tomcat 7 or 8. You might need to distribute other classes when working with different servlet containers, but the APIs shouldn't change much.

Hereinafter the created sessions are called subsessions , the original container-managed session is the parent session... The implementation consists of the following five classes:

The SingleSessionManager keeps track of the creation, propagation, and cleanup of all subsections. It does this by acting like a Servlet filter, which replaces the ServletRequest with a wrapper that returns the appropriate hook. The scheduler periodically checks for expired subnets ... and yes, it's a singleton. Sorry, but I still like them.

package session;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

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.HttpSession;

/**
 * A singleton class that manages multiple sessions on top of a regular container managed session.
 * See web.xml for information on how to enable this.
 *
 */
public class SingleSessionManager implements Filter {

    /**
     * The default session timeout in seconds to be used if no explicit timeout is provided.
     */
    public static final int DEFAULT_TIMEOUT = 900;

    /**
     * The default interval for session validation checks in seconds to be used if no explicit
     * timeout is provided.
     */
    public static final int DEFAULT_SESSION_INVALIDATION_CHECK = 15;

    private static SingleSessionManager instance;

    private ScheduledExecutorService scheduler;
    protected int timeout;
    protected long sessionInvalidationCheck;

    private Map<SubSessionKey, HttpSessionWrapper> sessions = new ConcurrentHashMap<SubSessionKey, HttpSessionWrapper>();

    public SingleSessionManager() {
        sessionInvalidationCheck = DEFAULT_SESSION_INVALIDATION_CHECK;
        timeout = DEFAULT_TIMEOUT;
    }

    public static SingleSessionManager getInstance() {
        if (instance == null) {
            instance = new SingleSessionManager();
        }
        return instance;
    }

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper((HttpServletRequest) request);
        chain.doFilter(wrapper,  response);
    }

    @Override
    public void init(FilterConfig cfg) throws ServletException {
        String timeout = cfg.getInitParameter("sessionTimeout");
        if (timeout != null && !timeout.trim().equals("")) {
            getInstance().timeout = Integer.parseInt(timeout) * 60;
        }

        String sessionInvalidationCheck = cfg.getInitParameter("sessionInvalidationCheck");
        if (sessionInvalidationCheck != null && !sessionInvalidationCheck.trim().equals("")) {
            getInstance().sessionInvalidationCheck = Long.parseLong(sessionInvalidationCheck);
        }

        getInstance().startSessionExpirationScheduler();
    }

    /**
     * Create a new session ID.
     * 
     * @return A new unique session ID.
     */
    public String generateSessionId() {
        return UUID.randomUUID().toString();
    }

    protected void startSessionExpirationScheduler() {
        if (scheduler == null) {
            scheduler = Executors.newScheduledThreadPool(1);
            final Runnable sessionInvalidator = new Runnable() {
                public void run() {
                    SingleSessionManager.getInstance().destroyExpiredSessions();
                }
            };
            final ScheduledFuture<?> sessionInvalidatorHandle =
                    scheduler.scheduleAtFixedRate(sessionInvalidator
                            , this.sessionInvalidationCheck
                            , this.sessionInvalidationCheck
                            , TimeUnit.SECONDS);
        }
    }

    /**
     * Get the timeout after which a session will be invalidated.
     * 
     * @return The timeout of a session in seconds.
     */
    public int getSessionTimeout() {
        return timeout;
    }

    /**
     * Retrieve a session.
     * 
     * @param uiid
     *            The user id this session is to be associated with.
     * @param create
     *            If <code>true</code> and no session exists for the given user id, a new session is
     *            created and associated with the given user id. If <code>false</code> and no
     *            session exists for the given user id, no new session will be created and this
     *            method will return <code>null</code>.
     * @param originalSession
     *            The original backing session created and managed by the servlet container.
     * @return The session associated with the given user id if this session exists and/or create is
     *         set to <code>true</code>, <code>null</code> otherwise.
     */
    public HttpSession getSession(String uiid, boolean create, HttpSession originalSession) {
        if (uiid != null) {
            SubSessionKey key = new SubSessionKey(originalSession.getId(), uiid);
            if (!sessions.containsKey(key) && create) {
                HttpSessionWrapper sw = new HttpSessionWrapper(uiid, originalSession);
                sessions.put(key, sw);
            }
            HttpSessionWrapper session = sessions.get(key);
            session.setLastAccessedTime(System.currentTimeMillis());
            return session;
        }
        return null;
    }

    public HttpSessionWrapper removeSession(SubSessionKey key) {
        return sessions.remove(key);
    }

    /**
     * Destroy a session, freeing all it resources.
     * 
     * @param session
     *            The session to be destroyed.
     */
    public void destroySession(HttpSessionWrapper session) {
        String uiid = ((HttpSessionWrapper)session).getUiid();
        SubSessionKey key = new SubSessionKey(session.getOriginalSession().getId(), uiid);
        HttpSessionWrapper w = getInstance().removeSession(key);
        if (w != null) {
            System.out.println("Session " + w.getId() + " with uiid " + uiid + " was destroyed.");
        } else {
            System.out.println("uiid " + uiid + " does not have a session.");
        }
    }

    /**
     * Destroy all session that are expired at the time of this method call.
     */
    public void destroyExpiredSessions() {
        List<HttpSessionWrapper> markedForDelete = new ArrayList<HttpSessionWrapper>();
        long time = System.currentTimeMillis() / 1000;
        for (HttpSessionWrapper session : sessions.values()) {
            if (time - (session.getLastAccessedTime() / 1000) >= session.getMaxInactiveInterval()) {
                markedForDelete.add(session);
            }
        }
        for (HttpSessionWrapper session : markedForDelete) {
            destroySession(session);
        }
    }

    /**
     * Remove all subsessions that were created from a given parent session.
     * 
     * @param originalSession
     *            All subsessions created with this session as their parent session will be
     *            invalidated.
     */
    public void clearAllSessions(HttpSession originalSession) {
        Iterator<HttpSessionWrapper> it = sessions.values().iterator();
        while (it.hasNext()) {
            HttpSessionWrapper w = it.next();
            if (w.getOriginalSession().getId().equals(originalSession.getId())) {
                destroySession(w);
            }
        }
    }

    public void setSessionTimeout(int timeout) {
        this.timeout = timeout;
    }

}

      

Subsection is identified by SubSessionKey . These key objects depend on the uiid and the parent session id.

package session;

/**
 * Key object for identifying a subsession.
 *
 */
public class SubSessionKey {

    private String sessionId;
    private String uiid;

    /**
     * Create a new instance of {@link SubSessionKey}.
     * 
     * @param sessionId
     *            The session id of the parent session.
     * @param uiid
     *            The users id this session is associated with.
     */
    public SubSessionKey(String sessionId, String uiid) {
        super();
        this.sessionId = sessionId;
        this.uiid = uiid;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((sessionId == null) ? 0 : sessionId.hashCode());
        result = prime * result + ((uiid == null) ? 0 : uiid.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        SubSessionKey other = (SubSessionKey) obj;
        if (sessionId == null) {
            if (other.sessionId != null)
                return false;
        } else if (!sessionId.equals(other.sessionId))
            return false;
        if (uiid == null) {
            if (other.uiid != null)
                return false;
        } else if (!uiid.equals(other.uiid))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "SubSessionKey [sessionId=" + sessionId + ", uiid=" + uiid + "]";
    }

}

      

The HttpServletRequestWrapper wraps the HttpServletRequest object. All methods are redirected to the wrapped request, with the exception of methods getSession

that return HttpSessionWrapper

based on the user id in those request parameters.

package session;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * Wrapper class that wraps a {@link HttpServletRequest} object. All methods are redirected to the
 * wrapped request except for the <code>getSession</code> which will return an
 * {@link HttpSessionWrapper} depending on the user id in this request parameters.
 *
 */
public class HttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper {

    private HttpServletRequest req;

    public HttpServletRequestWrapper(HttpServletRequest req) {
        super(req);
        this.req = req;
    }

    @Override
    public HttpSession getSession() {
        return getSession(true);
    }

    @Override
    public HttpSession getSession(boolean create) {
        String[] uiid = getParameterMap().get("uiid");
        if (uiid != null && uiid.length >= 1) {
            return SingleSessionManager.getInstance().getSession(uiid[0], create, req.getSession(create));
        }
        return req.getSession(create);
    }
}

      

HttpSessionWrapper represents jogging.

package session;

import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;

/**
 * Implementation of a HttpSession. Each instance of this class is created around a container
 * managed parent session with it lifetime linked to it parent's.
 *
 */
@SuppressWarnings("deprecation")
public class HttpSessionWrapper implements HttpSession {

    private Map<String, Object> attributes;
    private Map<String, Object> values;
    private long creationTime;
    private String id;
    private String uiid;
    private boolean isNew;
    private long lastAccessedTime;
    private HttpSession originalSession;

    public HttpSessionWrapper(String uiid, HttpSession originalSession) {
        creationTime = System.currentTimeMillis();
        lastAccessedTime = creationTime;
        id = SingleSessionManager.getInstance().generateSessionId();
        isNew = true;
        attributes = new HashMap<String, Object>();
        Enumeration<String> names = originalSession.getAttributeNames();
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            attributes.put(name, originalSession.getAttribute(name));
        }
        values = new HashMap<String, Object>();
        for (String name : originalSession.getValueNames()) {
            values.put(name, originalSession.getValue(name));
        }
        this.uiid = uiid;
        this.originalSession = originalSession;
    }

    public String getUiid() {
        return uiid;
    }

    public void setNew(boolean b) {
        isNew = b;
    }

    public void setLastAccessedTime(long time) {
        lastAccessedTime = time;
    }

    @Override
    public Object getAttribute(String arg0) {
        return attributes.get(arg0);
    }

    @Override
    public Enumeration<String> getAttributeNames() {
        return Collections.enumeration(attributes.keySet());
    }

    @Override
    public long getCreationTime() {
        return creationTime;
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public long getLastAccessedTime() {
        return lastAccessedTime;
    }

    @Override
    public int getMaxInactiveInterval() {
        return SingleSessionManager.getInstance().getSessionTimeout();
    }

    @Override
    public ServletContext getServletContext() {
        return originalSession.getServletContext();
    }

    @Override
    public HttpSessionContext getSessionContext() {
        return new HttpSessionContext() {

            @Override
            public Enumeration<String> getIds() {
                return Collections.enumeration(new HashSet<String>());
            }

            @Override
            public HttpSession getSession(String arg0) {
                return null;
            }

        };
    }

    @Override
    public Object getValue(String arg0) {
        return values.get(arg0);
    }

    @Override
    public String[] getValueNames() {
        return values.keySet().toArray(new String[values.size()]);
    }

    @Override
    public void invalidate() {
        SingleSessionManager.getInstance().destroySession(this);
    }

    @Override
    public boolean isNew() {
        return isNew;
    }

    @Override
    public void putValue(String arg0, Object arg1) {
        values.put(arg0, arg1);
    }

    @Override
    public void removeAttribute(String arg0) {
        attributes.remove(arg0);
    }

    @Override
    public void removeValue(String arg0) {
        values.remove(arg0);
    }

    @Override
    public void setAttribute(String arg0, Object arg1) {
        attributes.put(arg0, arg1);
    }

    @Override
    public void setMaxInactiveInterval(int arg0) {
        SingleSessionManager.getInstance().setSessionTimeout(arg0);
    }

    public HttpSession getOriginalSession() {
        return originalSession;
    }

}

      

SessionInvalidator HttpSessionListener

, which takes care of clearing all subnets in case the parent session is invalid.

package session;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * Session listener that listens for the destruction of a container managed session and takes care
 * of destroying all it subsessions.
 * <p>
 * Normally this listener won't have much to do since subsessions usually have a shorter lifetime
 * than their parent session and therefore will timeout long before this method is called. This
 * listener will only be important in case of an explicit invalidation of a parent session.
 * </p>
 *
 */
public class SessionInvalidator implements HttpSessionListener {

    @Override
    public void sessionCreated(HttpSessionEvent arg0) {
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent arg0) {
        SingleSessionManager.getInstance().clearAllSessions(arg0.getSession());
    }

}

      

Include everything by putting the following in your web.xml

<filter>
  <filter-name>SingleSessionFilter</filter-name>
  <filter-class>de.supportgis.sgjWeb.session.SingleSessionManager</filter-class>
  <!-- The timeout in minutes after which a subsession will be invalidated. It is recommended to set a session timeout for the servled container using the parameter "session-timeout", which is higher than this value. -->
  <init-param>
    <param-name>sessionTimeout</param-name>
    <param-value>1</param-value>
  </init-param>
  <init-param>
    <!-- The intervall in seconds in which a check for expired sessions will be performed. -->
    <param-name>sessionInvalidationCheck</param-name>
    <param-value>15</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>SingleSessionFilter</filter-name>
  <!-- Insert the name of your servlet here to which the session management should apply, or use url-pattern instead. --> 
  <servlet-name>YourServlet</servlet-name>
</filter-mapping>
<listener>
  <listener-class>session.SessionInvalidator</listener-class>
</listener>

<!-- Timeout of the parent session -->
<session-config>
  <session-timeout>40</session-timeout>
  <!-- Session timeout interval in minutes -->
</session-config>
      

Run codeHide result


0


source







All Articles