Session
在servlet中Session对象是由javax.servlet.http.HttpSession接口来表示的。这个接口的实现类为org.apache.catalina.session包中的StandardSession类。然而出于安全考虑管理器并不会将StandardSession实例直接传递给servlet,它会传递一个门面类StandardSessionFacade。
Session Interface
package org.apache.catalina; import java.io.IOException;
import java.security.Principal;
import java.util.Iterator;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
public interface Session {
public static final String SESSION_CREATED_EVENT = "createSession";
public static final String SESSION_DESTROYED_EVENT = "destroySession";
public String getAuthType();
public long getCreationTime();
public void setCreationTime(long time);
public void setId(String id);
public String getInfo();
public long getLastAccessedTime();
public Manager getManager();
public int getMaxInactiveInterval();
public void setMaxInactiveInterval(int interval);
public Principal getPrincipal();
public void setPrincipal(Principal principal);
public HttpSession getSession();
public boolean isValid();
public void access();
public void expire();
public Iterator getNoteNames();
public void removeNote(String name);
public void setNote(String name, Object value); }
The StandardSession Class
StandardSession类是Session接口的标准实现,另外在实现了javax.servlet.http.HttpSession和org.apache.catalina.Session接口的同时StandardSession还实现了java.lang.Serializable接口,以方便对Session对象时行序列化。
这个类的构造函数强制要求传一个Manager对象,强制每个Session都于一个Manager关联。
public StandardSession(Manager manager);
getSession方法会通过当前对象创建它的一个门面类StandardSessionFacade。
public HttpSession getSession() {
if (facade == null)
facade = new StandardSessionFacade(this);
return (facade);
}
Manager
管理 Session对象,它负责生成Session对象并在需要的时候销毁这些对象。一个Manager由org.apache.catalina.Manager接口来表示。在Catalina中,org.apache.catalina.session包的ManagerBase类提供基本功能的实现,ManagerBase有两个子类:StandardManager和PersistentManagerBase。
运行时,StandardManager将Session对象保存在内存中。当其停止时,它会将当前所有的Session对象保存一个文件中,再次启动时再重新加载这些对象。
PersistentManagerBase是Manager组件的一个基础类,它将Session对象保存在一个二级储存中。它有两个子类:PersistentManager和DistributedManager(DistributedManager只在Tomcat4中使用)
Manager Interface
package org.apache.catalina;
import java.beans.PropertyChangeListener;
import java.io.IOException;
public interface Manager {
public Container getContainer();
public void setContainer(Container container);
public void setDefaultContext(DefaultContext defaultContext);
public boolean getDistributable();
public void setDistributable(boolean distributable);
public String getInfo();
public int getMaxInactiveInterval();
public void setMaxInactiveInterval(int interval);
public void add(Session session);
public void addPropertyChangeListener(PropertyChangeListener listener);
public Session findSession(String id) throws IOException;
public Session[] findSessions();
public void load() throws ClassNotFoundException, IOException;
public void remove(Session session);
public void removePropertyChangeListener(PropertyChangeListener listener);
public void unload() throws IOException;
}
首先,Manager接口中的setContainer和getContainer方法用来将Manager的实现类同Context关联。createSession方法用来创建一个Session对象。add方法用于在Session池中添加一个Session对象remove方法用于在Session池中删除一个Session对象。getMaxInactiveInterval方法和setMaxInactiveInterval方法用于获取或是设置Session对象最大的生存周期。
最后,load和unload方法用于将Session对象保存在一个二级储存中。
ManagerBase Class
ManagerBase是一个抽象类,所有的Manager实现类都需要继承这个类。ManagerBase为它子类实现了部分基本功能。另外,ManagerBase类中的createSession方法创建Session对象。每一个Session都有一个唯一的标识符。ManagerBase类中的protected方法generateSessionId可以返回一个唯一标识符。
对于一个Context中的Manager实例,它负责所有在Context中活跃的Session对象,这些活跃的Session对象被保存在一个名为sessions的HashMap中
protected HashMap sessions = new HashMap();
add方法会添加一个Session对象到HashMap中
public void add(Session session) {
synchronized (sessions) {
sessions.put(session.getId(), session);
}
}
remove方法会从HashMap中删除一个指定的Session
public void remove(Session session) {
synchronized (sessions) {
sessions.remove(session.getId());
}
}
无参的findSession方法会以数组形式返回所有在HashMap中活跃的Session对象,以SessionId为参的findSession方法会返回指定的Session对象。
public Session[] findSessions() {
Session results[] = null;
synchronized (sessions) {
results = new Session[sessions.size()];
results = (Session[]) sessions.values().toArray(results);
}
return (results);
}
public Session findSession(String id) throws IOException {
if (id == null)
return (null);
synchronized (sessions) {
Session session = (Session) sessions.get(id);
return (session);
}
}
StandardManager
StandardManager类是Manager接口的标准实现,它将Session对象保存在内存中。它实现了LifeCycel接口,所以它可以被启动或是停用。在它的stop方法实现中,会调用unload方法序列化所有有效的Session对象到一个名为SESSIONS.ser的文件中。这个文件可以在CATALINA_HOME下找到。当这个Manager重新启动时就会调用load方法重新加载这些Session对象。
Manager同进负责销毁无效的Session对象,在Tomcat4中会有一个专门的线程来负责。所以StandardManager实现了java.lang.Runnable接口。下面是在Tomcat4中StandardManager类的run方法
public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processExpires();
}
}
threadSleep方法使线程休眠由checkInterval变量指定的时间,默认值为60,可以调用setCheckInterval方法来重新设置这个值。
processExpire方法循环遍历所有Manager管理的Session对象,对比这些Session对象的失效时间与当前时间,对于失效的Session对象直接调用Session接口的expire方法。maxInactiveInternal变量可以通过setMaxInactiveInterval方法来修改。
在Tomcat5中,StandardManager没有实现java.lang.Runnable接口。它的processExpires方法直接由backgroundprocess方法调用
public void backgroundProcess() {
processExpires();
}
backgroundProcess方法由org.apache.catalina.core.StandardContext类的backgroundProcess方法调用,这个将在后面的章节中讨论。
PersistentManagerBase
PersistentManagerBase是所有持久化Manager的父类,和StandardManager类的唯一区别就是它会持久化储存Session对象。PersistentManagerBase类使用了一个store的私有变量来保存所有Session对象,store代表一个二级储存。
private Store store = null;
在一个持久化的Manager中,Session对象可以被备份或是换出。当一个Session对象被备份时,Session对象会被复制到Store中同时它还会继续保留在内存中。因此,如果服务器宕机的话,还可以从Store中将这些Session对象恢复出来。当Session对象数量超过了一个指定的数值或是一个Session对象空闲太久的时候,它就会被换出。换出的主要目的就是为了节约内存。
在Tomcat4中,PersistentManagerBase实现了java.lang.Runnable接口,使用一个单独的线程定时去备份或是换出活跃的Session对象。
Swap Out
PersistentManagerBase会执行一系列来规则来确定是否换出Session对象。一般一个Session对象被换出是因为当前Session对象数量超过的当前所能容纳的最大值,或是一个Session对象长时间末被使用。
当拥有大量的Session对象时,PersistentManagerBase实例会随机换出Session对象直至Session数量等于可容纳Session对象的最大值。
当一个Session对象闲置太长时间时,PersistentManagerBase使用了两个变量minIdleSwap和maxIdleSwap来决定这个Session是否被换出。如果这个Session的lastAccessedTime变量值同时超过了minIdleSwap和maxIdleSwap变量的值,那么这个Session将会被换出。为了防止任何Session被换出,你可以为maxIdleSwap变量设置 一个负数的值。
由于一个活跃的Session可以被换出,它即可以在内存中也可以在Store中。因此,findSession (String id)方法会首先在内存中查找Session,如果内存中没有,再去Store中查找。
public Session findSession(String id) throws IOException {
Session session = super.findSession(id);
if (session != null) return (session);
// not found in memory, see if the Session is in the Store
// swapIn returns an active session in the Store
session = swapIn(id);
return (session);
}
Back-up
并不是所有活跃的Session都需要备份。PersistentManagerBase只会备份那些闲置时间超过maxIdleBackup变量值的Session对象。processMaxIdleBackups方法会执行Session的备份工作。
PersistentManager
PersistentManager继承自PersistentManagerBase。
package org.apache.catalina.session;
public final class PersistentManager extends PersistentManagerBase {
// The descriptive information about this implementation.
private static final String info = "PersistentManager/1.0";
// The descriptive name of this Manager implementation (for logging).
protected static String name = "PersistentManager";
public String getInfo() {
return (this.info);
}
public String getName() {
return (name);
}
}