Tomcat4的session管理器相关代码整理《how tomcat works》

作者: admin 分类: Tomcat 发布时间: 2019-11-20 14:12  阅读: 36 views

Catalina通过Session管理器的组件来管理建立的session对象,有org.apache.catalina.Manager接口表示。session管理器负责创建、更新、销毁session对象。当有请求来,要会返回一个有效的session对象。默认情况下,Session管理器会将其管理的session对象放在内存中。当然,也可以将它持久化写入到文件或数据库中。

HttpservletReqeust对象通过Manager获取session方法


public class HttpRequestBase extends RequestBase implements HttpRequest, HttpServletRequest { private HttpSession 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.getSession()); // 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) { return (session.getSession()); } } // Create a new session if requested and the response is not committed if (!create) return (null); if ((context != null) && (response != null) && context.getCookies() && response.getResponse().isCommitted()) { throw new IllegalStateException (sm.getString("httpRequestBase.createCommitted")); } session = manager.createSession(); if (session != null) return (session.getSession()); else return (null); } ... }

session接口

session接口是作为catalina内部的外观类使用的,定义如下:


package org.apache.catalina; import java.io.IOException; import java.security.Principal; import java.util.Iterator; import javax.servlet.ServletException; public interface Session { public static final String SESSION_CREATED_EVENT = "createSession"; public static final String SESSION_DESTROYED_EVENT = "destroySession"; public String getAuthType(); public void setAuthType(String authType); public long getCreationTime(); public void setCreationTime(long time); public String getId(); public void setId(String id); public String getInfo(); public long getLastAccessedTime(); public Manager getManager(); public void setManager(Manager manager); public int getMaxInactiveInterval(); public void setMaxInactiveInterval(int interval); public void setNew(boolean isNew); public Principal getPrincipal(); public void setPrincipal(Principal principal); public HttpSession getSession(); public void setValid(boolean isValid); public boolean isValid(); public void access(); public void addSessionListener(SessionListener listener); public void expire(); public Object getNote(String name); public Iterator getNoteNames(); public void recycle(); public void removeNote(String name); public void removeSessionListener(SessionListener listener); public void setNote(String name, Object value); }

standardSession 部分 (session的标准实现)

## 通过外观模式获取session实例,防止servlet访问一些敏感方法(HttpSession不能想下转换为StandardSession类型)

public HttpSession getSession()
{
if (this.facade == null) {
  this.facade = new StandardSessionFacade(this);
}
return this.facade;
}

## 过期
public void expire(boolean notify)
  {
    if (this.expiring) {
      return;
    }
    this.expiring = true;
    setValid(false);
    if (this.manager != null) {
      this.manager.remove(this);
    }
    String[] keys = keys();
    for (int i = 0; i < keys.length; i++) {
      removeAttribute(keys[i], notify);
    }
    if (notify) {
      fireSessionEvent("destroySession", null);
    }
    Context context = (Context)this.manager.getContainer();
    Object[] listeners = context.getApplicationListeners();
    if ((notify) && (listeners != null))
    {
      HttpSessionEvent event = new HttpSessionEvent(getSession());
      for (int i = 0; i < listeners.length; i++)
      {
        int j = listeners.length - 1 - i;
        if ((listeners[j] instanceof HttpSessionListener))
        {
          HttpSessionListener listener = (HttpSessionListener)listeners[j];
          try
          {
            fireContainerEvent(context, "beforeSessionDestroyed", listener);

            listener.sessionDestroyed(event);
            fireContainerEvent(context, "afterSessionDestroyed", listener);
          }
          catch (Throwable t)
          {
            try
            {
              fireContainerEvent(context, "afterSessionDestroyed", listener);
            }
            catch (Exception e) {}
            log(sm.getString("standardSession.sessionEvent"), t);
          }
        }
      }
    }
    this.expiring = false;
    if ((this.manager != null) && ((this.manager instanceof ManagerBase))) {
      recycle();
    }
  }

Mananger 接口(session管理器是它的实例)

Manager接口提供了getContainer()方法和setContainer()方法,可以将一个Manager实现与一个Context容器相关联。
createSession()方法用来创建一个Session实例,
add()方法会将一个session实例添加到session池中,
remove()方法则会将一个session实例从session中移除。
getMaxInactiveInterval()方法和setMaxInactiveInterval()方法用来获取、设置一个时间长度,session管理器会以此值作为一个session对象的最长存活时间。
load()方法会将介质中的session对象重新载入到内存中。
unload()方法会将当前活动的session对象存储到manager指定的介质中。


package org.apache.catalina; import java.beans.PropertyChangeListener; import java.io.IOException; public abstract interface Manager { public abstract Container getContainer(); public abstract void setContainer(Container paramContainer); public abstract DefaultContext getDefaultContext(); public abstract void setDefaultContext(DefaultContext paramDefaultContext); public abstract boolean getDistributable(); public abstract void setDistributable(boolean paramBoolean); public abstract String getInfo(); public abstract int getMaxInactiveInterval(); public abstract void setMaxInactiveInterval(int paramInt); public abstract void add(Session paramSession); public abstract Session createEmptySession(); public abstract void addPropertyChangeListener(PropertyChangeListener paramPropertyChangeListener); public abstract Session createSession(); public abstract Session findSession(String paramString) throws IOException; public abstract Session[] findSessions(); public abstract void load() throws ClassNotFoundException, IOException; public abstract void remove(Session paramSession); public abstract void removePropertyChangeListener(PropertyChangeListener paramPropertyChangeListener); public abstract void unload() throws IOException; }

ManagerBase类

是一个抽象类,所有的session管理器组件都会继承此类。
createSession()方法会创建一个新的session对象。
generateSessionId()方法会返回一个唯一的标识符。
Context容器的session管理器会将活动的session对象存储在一个名为sessionss的HashMap变量中。
add()方法会将一个Session对象添加到HashMap变量session中。
remove()方法会将一个session对象从HashMap变量sessions中移除。
findSession()方法会返回所有活动/自定的session对象。

package org.apache.catalina.session;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.io.PrintStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
import javax.servlet.http.HttpSession;
import org.apache.catalina.Container;
import org.apache.catalina.DefaultContext;
import org.apache.catalina.Engine;
import org.apache.catalina.Logger;
import org.apache.catalina.Manager;
import org.apache.catalina.Session;
import org.apache.catalina.util.StringManager;

public abstract class ManagerBase
  implements Manager
{
  protected static final String DEFAULT_ALGORITHM = "MD5";
  protected static final int SESSION_ID_BYTES = 16;
  protected String algorithm = "MD5";
  protected Container container;
  protected int debug = 0;
  protected DefaultContext defaultContext = null;
  protected MessageDigest digest = null;
  protected boolean distributable;
  protected String entropy = null;
  private static final String info = "ManagerBase/1.0";
  protected int maxInactiveInterval = 60;
  protected static String name = "ManagerBase";
  protected Random random = null;
  protected String randomClass = "java.security.SecureRandom";
  protected ArrayList recycled = new ArrayList();
  protected HashMap sessions = new HashMap();
  protected int sessionCounter = 0;
  protected int maxActive = 0;
  protected int duplicates = 0;
  protected static StringManager sm = StringManager.getManager("org.apache.catalina.session");
  protected PropertyChangeSupport support = new PropertyChangeSupport(this);

  public String getAlgorithm()
  {
    return this.algorithm;
  }

  public void setAlgorithm(String algorithm)
  {
    String oldAlgorithm = this.algorithm;
    this.algorithm = algorithm;
    this.support.firePropertyChange("algorithm", oldAlgorithm, this.algorithm);
  }

  public Container getContainer()
  {
    return this.container;
  }

  public void setContainer(Container container)
  {
    Container oldContainer = this.container;
    this.container = container;
    this.support.firePropertyChange("container", oldContainer, this.container);
  }

  public DefaultContext getDefaultContext()
  {
    return this.defaultContext;
  }

  public void setDefaultContext(DefaultContext defaultContext)
  {
    DefaultContext oldDefaultContext = this.defaultContext;
    this.defaultContext = defaultContext;
    this.support.firePropertyChange("defaultContext", oldDefaultContext, this.defaultContext);
  }

  public int getDebug()
  {
    return this.debug;
  }

  public void setDebug(int debug)
  {
    this.debug = debug;
  }

  public synchronized MessageDigest getDigest()
  {
    if (this.digest == null)
    {
      if (this.debug >= 1) {
        log(sm.getString("managerBase.getting", this.algorithm));
      }
      try
      {
        this.digest = MessageDigest.getInstance(this.algorithm);
      }
      catch (NoSuchAlgorithmException e)
      {
        log(sm.getString("managerBase.digest", this.algorithm), e);
        try
        {
          this.digest = MessageDigest.getInstance("MD5");
        }
        catch (NoSuchAlgorithmException f)
        {
          log(sm.getString("managerBase.digest", "MD5"), e);

          this.digest = null;
        }
      }
      if (this.debug >= 1) {
        log(sm.getString("managerBase.gotten"));
      }
    }
    return this.digest;
  }

  public boolean getDistributable()
  {
    return this.distributable;
  }

  public void setDistributable(boolean distributable)
  {
    boolean oldDistributable = this.distributable;
    this.distributable = distributable;
    this.support.firePropertyChange("distributable", new Boolean(oldDistributable), new Boolean(this.distributable));
  }

  public String getEntropy()
  {
    if (this.entropy == null) {
      setEntropy(toString());
    }
    return this.entropy;
  }

  public void setEntropy(String entropy)
  {
    String oldEntropy = entropy;
    this.entropy = entropy;
    this.support.firePropertyChange("entropy", oldEntropy, this.entropy);
  }

  public String getInfo()
  {
    return "ManagerBase/1.0";
  }

  public int getMaxInactiveInterval()
  {
    return this.maxInactiveInterval;
  }

  public void setMaxInactiveInterval(int interval)
  {
    int oldMaxInactiveInterval = this.maxInactiveInterval;
    this.maxInactiveInterval = interval;
    this.support.firePropertyChange("maxInactiveInterval", new Integer(oldMaxInactiveInterval), new Integer(this.maxInactiveInterval));
  }

  public String getName()
  {
    return name;
  }

  public synchronized Random getRandom()
  {
    if (this.random == null) {
      synchronized (this)
      {
        if (this.random == null)
        {
          log(sm.getString("managerBase.seeding", this.randomClass));
          long seed = System.currentTimeMillis();
          char[] entropy = getEntropy().toCharArray();
          for (int i = 0; i < entropy.length; i++)
          {
            long update = (byte)entropy[i] << i % 8 * 8;
            seed ^= update;
          }
          try
          {
            Class clazz = Class.forName(this.randomClass);
            this.random = ((Random)clazz.newInstance());
            this.random.setSeed(seed);
          }
          catch (Exception e)
          {
            log(sm.getString("managerBase.random", this.randomClass), e);

            this.random = new Random();
            this.random.setSeed(seed);
          }
          log(sm.getString("managerBase.complete", this.randomClass));
        }
      }
    }
    return this.random;
  }

  public String getRandomClass()
  {
    return this.randomClass;
  }

  public void setRandomClass(String randomClass)
  {
    String oldRandomClass = this.randomClass;
    this.randomClass = randomClass;
    this.support.firePropertyChange("randomClass", oldRandomClass, this.randomClass);
  }

  public void add(Session session)
  {
    synchronized (this.sessions)
    {
      this.sessions.put(session.getId(), session);
      if (this.sessions.size() > this.maxActive) {
        this.maxActive = this.sessions.size();
      }
    }
  }

  public void addPropertyChangeListener(PropertyChangeListener listener)
  {
    this.support.addPropertyChangeListener(listener);
  }

  public Session createSession()
  {
    Session session = createEmptySession();

    session.setNew(true);
    session.setValid(true);
    session.setCreationTime(System.currentTimeMillis());
    session.setMaxInactiveInterval(this.maxInactiveInterval);
    String sessionId = generateSessionId();

    String jvmRoute = getJvmRoute();
    if (jvmRoute != null) {
      sessionId = sessionId + '.' + jvmRoute;
    }
    synchronized (this.sessions)
    {
      while (this.sessions.get(sessionId) != null)
      {
        sessionId = generateSessionId();
        this.duplicates += 1;
        if (jvmRoute != null) {
          sessionId = sessionId + '.' + jvmRoute;
        }
      }
    }
    session.setId(sessionId);
    this.sessionCounter += 1;

    return session;
  }

  public Session createEmptySession()
  {
    Session session = null;
    synchronized (this.recycled)
    {
      int size = this.recycled.size();
      if (size > 0)
      {
        session = (Session)this.recycled.get(size - 1);
        this.recycled.remove(size - 1);
      }
    }
    if (session != null) {
      session.setManager(this);
    } else {
      session = new StandardSession(this);
    }
    return session;
  }

  public Session findSession(String id)
    throws IOException
  {
    if (id == null) {
      return null;
    }
    synchronized (this.sessions)
    {
      Session session = (Session)this.sessions.get(id);
      return session;
    }
  }

  public Session[] findSessions()
  {
    Session[] results = null;
    synchronized (this.sessions)
    {
      results = new Session[this.sessions.size()];
      results = (Session[])this.sessions.values().toArray(results);
    }
    return results;
  }

  public void remove(Session session)
  {
    synchronized (this.sessions)
    {
      this.sessions.remove(session.getId());
    }
  }

  public void removePropertyChangeListener(PropertyChangeListener listener)
  {
    this.support.removePropertyChangeListener(listener);
  }

  protected synchronized String generateSessionId()
  {
    Random random = getRandom();
    byte[] bytes = new byte[16];
    getRandom().nextBytes(bytes);
    bytes = getDigest().digest(bytes);

    StringBuffer result = new StringBuffer();
    for (int i = 0; i < bytes.length; i++)
    {
      byte b1 = (byte)((bytes[i] & 0xF0) >> 4);
      byte b2 = (byte)(bytes[i] & 0xF);
      if (b1 < 10) {
        result.append((char)(48 + b1));
      } else {
        result.append((char)(65 + (b1 - 10)));
      }
      if (b2 < 10) {
        result.append((char)(48 + b2));
      } else {
        result.append((char)(65 + (b2 - 10)));
      }
    }
    return result.toString();
  }

  public Engine getEngine()
  {
    Engine e = null;
    for (Container c = getContainer(); (e == null) && (c != null); c = c.getParent()) {
      if ((c != null) && ((c instanceof Engine))) {
        e = (Engine)c;
      }
    }
    return e;
  }

  public String getJvmRoute()
  {
    Engine e = getEngine();
    return e == null ? null : e.getJvmRoute();
  }

  void log(String message)
  {
    Logger logger = null;
    if (this.container != null) {
      logger = this.container.getLogger();
    }
    if (logger != null)
    {
      logger.log(getName() + "[" + this.container.getName() + "]: " + message);
    }
    else
    {
      String containerName = null;
      if (this.container != null) {
        containerName = this.container.getName();
      }
      System.out.println(getName() + "[" + containerName + "]: " + message);
    }
  }

  void log(String message, Throwable throwable)
  {
    Logger logger = null;
    if (this.container != null) {
      logger = this.container.getLogger();
    }
    if (logger != null)
    {
      logger.log(getName() + "[" + this.container.getName() + "] " + message, throwable);
    }
    else
    {
      String containerName = null;
      if (this.container != null) {
        containerName = this.container.getName();
      }
      System.out.println(getName() + "[" + containerName + "]: " + message);

      throwable.printStackTrace(System.out);
    }
  }

  void recycle(Session session)
  {
    synchronized (this.recycled)
    {
      this.recycled.add(session);
    }
  }

  public void setSessionCounter(int sessionCounter)
  {
    this.sessionCounter = sessionCounter;
  }

  public int getSessionCounter()
  {
    return this.sessionCounter;
  }

  public int getDuplicates()
  {
    return this.duplicates;
  }

  public void setDuplicates(int duplicates)
  {
    this.duplicates = duplicates;
  }

  public int getActiveSessions()
  {
    return this.sessions.size();
  }

  public int getMaxActive()
  {
    return this.maxActive;
  }

  public void setMaxActive(int maxActive)
  {
    this.maxActive = maxActive;
  }

  public String listSessionIds()
  {
    StringBuffer sb = new StringBuffer();
    Iterator keys = this.sessions.keySet().iterator();
    while (keys.hasNext()) {
      sb.append(keys.next()).append(" ");
    }
    return sb.toString();
  }

  public String getSessionAttribute(String sessionId, String key)
  {
    Session s = (Session)this.sessions.get(sessionId);
    if (s == null)
    {
      log("Session not found " + sessionId);
      return null;
    }
    Object o = s.getSession().getAttribute(key);
    if (o == null) {
      return null;
    }
    return o.toString();
  }

  public void expireSession(String sessionId)
  {
    Session s = (Session)this.sessions.get(sessionId);
    if (s == null)
    {
      log("Session not found " + sessionId);
      return;
    }
    s.expire();
  }

  public String getLastAccessedTime(String sessionId)
  {
    Session s = (Session)this.sessions.get(sessionId);
    if (s == null)
    {
      log("Session not found " + sessionId);
      return "";
    }
    return new Date(s.getLastAccessedTime()).toString();
  }
}

StandardManager

是Manager接口的标准实现,该类将session对存储于内存中。StandardManager类实现了Lifecycle接口,这样就可以由与其关联的Context容器来启动或关闭。
stop()方法会调用unload()方法,时间长度由变量checkInterval指定
threadSleep()方法会使线程休眠一段时间
processExpired()方法会遍历由session管理器管理的所有session对象,对比时间决定expire()的调用

代码省去贴

PersistentManagerBase

是所有持久化Session管理器的弗雷。StandardManager实例和持久化session管理器的区别在于后者中存储器的表现形式,用Store对象来表示。
在持久化session管理器中,session对象可以备份,也可以换出。
当备份一个session对象时,该session对象会被复制到存储器中,而原对象仍在内存中。

代码省去

存储器 Store 接口

存储器是org.apache.catalina.Store接口的实例,是session管理器管理的session对象提供持久化存储器的一个组件。

package org.apache.catalina;

import java.beans.PropertyChangeListener;
import java.io.IOException;

public abstract interface Store
{
  public abstract String getInfo();

  public abstract Manager getManager();

  public abstract void setManager(Manager paramManager);

  public abstract int getSize()
    throws IOException;

  public abstract void addPropertyChangeListener(PropertyChangeListener paramPropertyChangeListener);

  public abstract String[] keys()
    throws IOException;

  public abstract Session load(String paramString)
    throws ClassNotFoundException, IOException;

  public abstract void remove(String paramString)
    throws IOException;

  public abstract void clear()
    throws IOException;

  public abstract void removePropertyChangeListener(PropertyChangeListener paramPropertyChangeListener);

  public abstract void save(Session paramSession)
    throws IOException;
}

StoreBase 及两个子类 FileStore和 JDBCStore

StoreBase是一个抽象类,会使用另一个线程周期性地检查session对象,从活动的session集合中移除session对象。

FileStore类会将session对象存储到某个文件中。
JDBCStore类将session对象通过JDBC存储数据库中。

##开启一个线程检查session
 public void run()
  {
    while (!this.threadDone)
    {
      threadSleep();
      processExpires();
    }
  }

##移除过期session对象
 protected void processExpires()
  {
    long timeNow = System.currentTimeMillis();
    String[] keys = null;
    if (!this.started) {
      return;
    }
    try
    {
      keys = keys();
    }
    catch (IOException e)
    {
      log(e.toString());
      e.printStackTrace();
      return;
    }
    for (int i = 0; i < keys.length; i++) {
      try
      {
        StandardSession session = (StandardSession)load(keys[i]);
        if (session != null) {
          if (session.isValid())
          {
            int maxInactiveInterval = session.getMaxInactiveInterval();
            if (maxInactiveInterval >= 0)
            {
              int timeIdle = (int)((timeNow - session.getLastUsedTime()) / 1000L);
              if (timeIdle >= maxInactiveInterval)
              {
                if (((PersistentManagerBase)this.manager).isLoaded(keys[i])) {
                  session.recycle();
                } else {
                  session.expire();
                }
                remove(session.getId());
              }
            }
          }
        }
      }
      catch (Exception e)
      {
        log("Session: " + keys[i] + "; " + e.toString());
        try
        {
          remove(keys[i]);
        }
        catch (IOException e2)
        {
          log(e2.toString());
          e2.printStackTrace();
        }
      }
    }
  }


   原创文章,转载请标明本文链接: Tomcat4的session管理器相关代码整理《how tomcat works》

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

更多阅读