Tomcat source code analysis-Session management analysis (II), tomcatsession

Source: Internet
Author: User

Tomcat source code analysis-Session management analysis (II), tomcatsession
Preface

In TOMCAT source code analysis-SESSION management analysis (I), I introduced the Session and Session manager, and introduced the initialization and startup of the Session Manager using StandardManager as an example, this article introduces other content of Session management.

Session Allocation

At the end of the article TOMCAT source code analysis-request Principle Analysis (below), we introduced the Filter responsibility chain. The requests received by Tomcat will go through the Filter responsibility chain, finally, it is handed over to the specific Servlet for processing. Taking the path http: // localhost: 8080/host-manager as an example, we can clearly see the responsibility chain of the Filter in the entire call stack (as shown in 1) and the subsequent JspServlet, finally, org. apache. catalina. connector. the getSession method of the Request.

Figure 1 request Stack

The getSession method of the Request (see Code List 1) is used to obtain the Session information corresponding to the current Request. If no Session information exists, a new Session is created.

Code List 1

    public HttpSession getSession(boolean create) {        Session session = doGetSession(create);        if (session == null) {            return null;        }                return session.getSession();    }

For the implementation of the doGetSession method, see Code List 2.

Code List 2

    protected Session doGetSession(boolean create) {        // There cannot be a session if no context has been assigned yet        if (context == null)            return (null);        // Return the current session if it exists and is valid        if ((session != null) && !session.isValid())            session = null;        if (session != null)            return (session);        // Return the requested session if it exists and is valid        Manager manager = null;        if (context != null)            manager = context.getManager();        if (manager == null)            return (null);      // Sessions are not supported        if (requestedSessionId != null) {            try {                session = manager.findSession(requestedSessionId);            } catch (IOException e) {                session = null;            }            if ((session != null) && !session.isValid())                session = null;            if (session != null) {                session.access();                return (session);            }        }        // Create a new session if requested and the response is not committed        if (!create)            return (null);        if ((context != null) && (response != null) &&            context.getServletContext().getEffectiveSessionTrackingModes().                    contains(SessionTrackingMode.COOKIE) &&            response.getResponse().isCommitted()) {            throw new IllegalStateException              (sm.getString("coyoteRequest.sessionCreateCommitted"));        }        // Attempt to reuse session id if one was submitted in a cookie        // Do not reuse the session id if it is from a URL, to prevent possible        // phishing attacks        // Use the SSL session ID if one is present.         if (("/".equals(context.getSessionCookiePath())                 && isRequestedSessionIdFromCookie()) || requestedSessionSSL ) {            session = manager.createSession(getRequestedSessionId());        } else {            session = manager.createSession(null);        }        // Creating a new session cookie based on that session        if ((session != null) && (getContext() != null)               && getContext().getServletContext().                       getEffectiveSessionTrackingModes().contains(                               SessionTrackingMode.COOKIE)) {            Cookie cookie =                ApplicationSessionCookieConfig.createSessionCookie(                        context, session.getIdInternal(), isSecure());                        response.addSessionCookieInternal(cookie);        }        if (session == null) {            return null;        }                session.access();        return session;    }

According to code list 2, the procedure for obtaining a Session is as follows:

Let's focus on reading the createSession method implemented by ManagerBase. For details, see code listing 3.

Code List 3

    public Session createSession(String sessionId) {                // Recycle or create a Session instance        Session session = createEmptySession();        // Initialize the properties of the new session and return it        session.setNew(true);        session.setValid(true);        session.setCreationTime(System.currentTimeMillis());        session.setMaxInactiveInterval(this.maxInactiveInterval);        if (sessionId == null) {            sessionId = generateSessionId();        }        session.setId(sessionId);        sessionCounter++;        return (session);    }

Now, we will introduce the creation and allocation of sessions.

Session tracking

HTTP is a connectionless protocol. If a client simply requests a file, the server does not need to know whether a series of requests come from the same client, in addition, you do not need to worry about whether the client is in the connection status. However, this communication protocol makes it difficult for the server to determine whether the connected client is the same person. When developing a Web program, we must try to combine relevant requests and maintain the user's status on the server. This leads to session tracking ).

The Tomcat tracing Session mainly uses its ID. Therefore, after receiving the request, you must obtain the Session ID corresponding to the request so that it can match the Session maintained in the StandardManager cache, achieve the effect of Session tracing. Do you still remember the postParseRequest method called when I introduced the service method of CoyoteAdapter in TOMCAT source code analysis-request principle analysis (in? There is such a piece of code, see code list 4.

Code list 4

If (request. getServletContext (). getinclutivesessiontrackingmodes (). contains (SessionTrackingMode. URL) {// Get the session ID if there was one String sessionID = request. getPathParameter (ApplicationSessionCookieConfig. getSessionUriParamName (request. getContext (); if (sessionID! = Null) {request. setRequestedSessionId (sessionID); request. setRequestedSessionURL (true) ;}/// no intermediate irrelevant code // Finally look for session ID in cookies and SSL session parseSessionCookiesId (req, request); parseSessionSslId (request ); return true ;}

 

According to code list 4, we can see that the steps for executing the postParseRequest method are as follows:

Obtain the maintained Session ID from the cache

In code list 4, call the getSessionUriParamName method (see Code List 5) to obtain the parameter name of the Session.

Code List 5

    public static String getSessionUriParamName(Context context) {                String result = getConfiguredSessionCookieName(context);                if (result == null) {            result = DEFAULT_SESSION_PARAMETER_NAME;         }                return result;     }

From the code list 2, we can see that the getSessionUriParamName method first calls the getConfiguredSessionCookieName method to obtain the Cookie name of the Session. If not, the default value is jsessionid (constant DEFAULT_SESSION_PARAMETER_NAME ). In code list 1, the value returned by the getSessionUriParamName method is used as the parameter of request. getPathParameter (see code list 6) to query the Session ID.

Code List 6

    protected String getPathParameter(String name) {        return pathParameters.get(name);    }
Obtain the Session ID from the Cookie contained in the request

The parseSessionCookiesId method called in code list 4 (see code list 7) is used to obtain the Session ID from the Cookie.

Code List 7

    protected void parseSessionCookiesId(org.apache.coyote.Request req, Request request) {        // If session tracking via cookies has been disabled for the current        // context, don't go looking for a session ID in a cookie as a cookie        // from a parent context with a session ID may be present which would        // overwrite the valid session ID encoded in the URL        Context context = (Context) request.getMappingData().context;        if (context != null && !context.getServletContext()                .getEffectiveSessionTrackingModes().contains(                        SessionTrackingMode.COOKIE))            return;                // Parse session id from cookies        Cookies serverCookies = req.getCookies();        int count = serverCookies.getCookieCount();        if (count <= 0)            return;        String sessionCookieName =            ApplicationSessionCookieConfig.getSessionCookieName(context);        for (int i = 0; i < count; i++) {            ServerCookie scookie = serverCookies.getCookie(i);            if (scookie.getName().equals(sessionCookieName)) {                // Override anything requested in the URL                if (!request.isRequestedSessionIdFromCookie()) {                    // Accept only the first session id cookie                    convertMB(scookie.getValue());                    request.setRequestedSessionId                        (scookie.getValue().toString());                    request.setRequestedSessionCookie(true);                    request.setRequestedSessionURL(false);                    if (log.isDebugEnabled())                        log.debug(" Requested cookie session id is " +                            request.getRequestedSessionId());                } else {                    if (!request.isRequestedSessionIdValid()) {                        // Replace the session id until one is valid                        convertMB(scookie.getValue());                        request.setRequestedSessionId                            (scookie.getValue().toString());                    }                }            }        }    }
Obtain the Session ID from SSL

The parseSessionSslId method called in code list 4 (see Code List 8) is used to obtain the Session ID from SSL.

Code List 8

    protected void parseSessionSslId(Request request) {        if (request.getRequestedSessionId() == null &&                SSL_ONLY.equals(request.getServletContext()                        .getEffectiveSessionTrackingModes()) &&                        request.connector.secure) {            // TODO Is there a better way to map SSL sessions to our sesison ID?            // TODO The request.getAttribute() will cause a number of other SSL            //      attribute to be populated. Is this a performance concern?            request.setRequestedSessionId(                    request.getAttribute(SSLSupport.SESSION_ID_KEY).toString());            request.setRequestedSessionSSL(true);        }    }
Session destruction

In the TOMCAT source code analysis-lifecycle management, we introduced the content related to container lifecycle management. StandardEngine is used as a container, the startInternal method is also called during the startup process (see code listing 9 ).

Code List 9

    @Override    protected synchronized void startInternal() throws LifecycleException {                // Log our server identification information        if(log.isInfoEnabled())            log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());        // Standard container startup        super.startInternal();    }

The startInternal method of StandardEngine actually represents the startInternal method of the ContainerBase parent class (see Code List 10 ).

Code List 10

    @Override    protected synchronized void startInternal() throws LifecycleException {        // Start our subordinate components, if any        if ((loader != null) && (loader instanceof Lifecycle))            ((Lifecycle) loader).start();        logger = null;        getLogger();        if ((logger != null) && (logger instanceof Lifecycle))            ((Lifecycle) logger).start();        if ((manager != null) && (manager instanceof Lifecycle))            ((Lifecycle) manager).start();        if ((cluster != null) && (cluster instanceof Lifecycle))            ((Lifecycle) cluster).start();        if ((realm != null) && (realm instanceof Lifecycle))            ((Lifecycle) realm).start();        if ((resources != null) && (resources instanceof Lifecycle))            ((Lifecycle) resources).start();        // Start our child containers, if any        Container children[] = findChildren();        for (int i = 0; i < children.length; i++) {            children[i].start();        }        // Start the Valves in our pipeline (including the basic), if any        if (pipeline instanceof Lifecycle)            ((Lifecycle) pipeline).start();        setState(LifecycleState.STARTING);        // Start our thread        threadStart();    }

In code list 10, each seed container is started at the beginning (because it has little to do with the content in this article, so it is not described much), and The threadStart method is called at last. For the implementation of threadStart, see code listing 11.

Code List 11

    protected void threadStart() {        if (thread != null)            return;        if (backgroundProcessorDelay <= 0)            return;        threadDone = false;        String threadName = "ContainerBackgroundProcessor[" + toString() + "]";        thread = new Thread(new ContainerBackgroundProcessor(), threadName);        thread.setDaemon(true);        thread.start();    }

The threadStart method starts a background thread and the task is ContainerBackgroundProcessor. The run method of ContainerBackgroundProcessor mainly calls the processChildren method. For details, refer to code list 12.

Code List 12

    protected class ContainerBackgroundProcessor implements Runnable {        public void run() {            while (!threadDone) {                try {                    Thread.sleep(backgroundProcessorDelay * 1000L);                } catch (InterruptedException e) {                    // Ignore                }                if (!threadDone) {                    Container parent = (Container) getMappingObject();                    ClassLoader cl =                         Thread.currentThread().getContextClassLoader();                    if (parent.getLoader() != null) {                        cl = parent.getLoader().getClassLoader();                    }                    processChildren(parent, cl);                }            }        }        protected void processChildren(Container container, ClassLoader cl) {            try {                if (container.getLoader() != null) {                    Thread.currentThread().setContextClassLoader                        (container.getLoader().getClassLoader());                }                container.backgroundProcess();            } catch (Throwable t) {                log.error("Exception invoking periodic operation: ", t);            } finally {                Thread.currentThread().setContextClassLoader(cl);            }            Container[] children = container.findChildren();            for (int i = 0; i < children.length; i++) {                if (children[i].getBackgroundProcessorDelay() <= 0) {                    processChildren(children[i], cl);                }            }        }    }

The processChildren method constantly iterates the sub-containers of StandardEngine and calls the backgroundProcess method of these sub-containers. Here, let's take a look at the implementation of the backgroundProcess of StandardEngine's Sun Tzu container StandardManager, that is, the backgroundProcess method of ManagerBase. For details, see the code list 13.

Code List 13

    public void backgroundProcess() {        count = (count + 1) % processExpiresFrequency;        if (count == 0)            processExpires();    }

BackgroundProcess implements a simple algorithm:

Count: Counter, starting with 0;

ProcessExpiresFrequency: The frequency of executing the processExpires method. The default value is 6.

For each execution of the backgroundProcess method, count increases by 1. processExpiresFrequency is called whenever the modulo of count + 1 and processExpiresFrequency is equal to 0. In short, the processExpiresFrequency method is executed once every time the backgroundProcess method is executed for a specified number of times. The implementation of processExpires is shown in code list 14.

Code list 14

    public void processExpires() {        long timeNow = System.currentTimeMillis();        Session sessions[] = findSessions();        int expireHere = 0 ;                if(log.isDebugEnabled())            log.debug("Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length);        for (int i = 0; i < sessions.length; i++) {            if (sessions[i]!=null && !sessions[i].isValid()) {                expireHere++;            }        }        long timeEnd = System.currentTimeMillis();        if(log.isDebugEnabled())             log.debug("End expire sessions " + getName() + " processingTime " + (timeEnd - timeNow) + " expired sessions: " + expireHere);        processingTime += ( timeEnd - timeNow );    }

The procedure for executing the processExpires method in listing 14 is as follows:

The standard implementation of the Session is StandardSession. The main function of its isValid method (see code listing 15) is to determine whether the Session has expired. For expired sessions, change its expiring status to true. The formula for judging expiration is:

(Current time-last access time of the Session)/1000)> = maximum access Interval

Code List 15

    public boolean isValid() {        if (this.expiring) {            return true;        }        if (!this.isValid) {            return false;        }        if (ACTIVITY_CHECK && accessCount.get() > 0) {            return true;        }        if (maxInactiveInterval >= 0) {             long timeNow = System.currentTimeMillis();            int timeIdle;            if (LAST_ACCESS_AT_START) {                timeIdle = (int) ((timeNow - lastAccessedTime) / 1000L);            } else {                timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L);            }            if (timeIdle >= maxInactiveInterval) {                expire(true);            }        }        return (this.isValid);    }
Summary

Tomcat manages sessions by creating, allocating, maintaining, tracing, and destroying sessions.

If you need to reprint, please indicate the author and source -- Author: jiaan. gja, the original article first: blog garden, original link: http://www.cnblogs.com/jiaan-geng/p/4920036.html

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.