Session管理
通过manager组件,catalina支持对会话(session)进行管理,manager组件表示为org.apache.catalina.Manager接口。管理器通常是与context一起协同工作。创建、修改和销毁session是manager的主要责任,当然它也负责像所有的request提供一个有效的session。
一个servlet可以通过调用javax.servlet.http.HttpServletRequest接口的getSession方法获取会话,方法的实现在org.apache.catalina.connector.HttpReqestBase类里面:
public HttpSession getSession() {
return (getSession(true));
}
public HttpSession getSession(boolean create) {
if( System.getSecurityManager() != null ) {
PrivilegedGetSession dp = new PrivilegedGetSession(create);
return (HttpSession)AccessController.doPrivileged(dp);
}
return doGetSession(create);
}
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进行持久化,保存到文件或数据库当中。
会话(Session)
在servlet编程当中,会话(session)对象用javax.servlet.http.HttpSession接口表示,其的基本实现类是org.apache.catalina.session包中StandardSession,出于安全考虑并不直接把该类提供给servlet,而是为其提供了一个门面类StandardSessionFacade,相关类图:
Session接口
Session是catalina内部门面,其实现类StandardSession同时也实现了javax.servlet.http.HttpSession接口,接口定义如下:
package org.apache.catalina;
import java.security.Principal;
import java.util.Iterator;
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 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);
}
Session对象通常包含在管理器中,所以这里需要一个setManager和getManager方法,以便把session分配给容器,每个session通常需要一个唯一标识。
StandardSession类
StandardSession类是Session接口的标准实现类,同时也实现了HttpSession接口,因为需要存储,它也是可序列号的,它的构造函数中需要提供一个Manager实例:
public StandardSession(Manager manager) {
super();
this.manager = manager;
if (manager instanceof ManagerBase)
this.debug = ((ManagerBase) manager).getDebug();
}
提供一个getSession方法会返回StandarSession的门面类:
public HttpSession getSession() {
if (facade == null)
facade = new StandardSessionFacade(this);
return (facade);
}
一旦在一个指定的时间内没有操作会话,那么session将会自动失效:
public void expire(boolean notify) {
// Mark this session as "being expired" if needed
if (expiring)
return;
expiring = true;
setValid(false);
// Remove this session from our manager's active sessions
if (manager != null)
manager.remove(this);
// Unbind any objects associated with this session
String keys[] = keys();
for (int i = 0; i < keys.length; i++)
removeAttribute(keys[i], notify);
// Notify interested session event listeners
if (notify) {
fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null);
}
// Notify interested application event listeners
// FIXME - Assumes we call listeners in reverse order
Context context = (Context) 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))
continue;
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) {;}
// FIXME - should we do anything besides log these?
log(sm.getString("standardSession.sessionEvent"), t);
}
}
}
// We have completed expire of this session
expiring = false;
if ((manager != null) && (manager instanceof ManagerBase)) {
recycle();
}
}
StandardSessionFacade类
为了防止servlet操作session接口中的一些方法,catalina中为StandardSession类提供了一个门面类,只允许servlet调用HttpSession中的方法。
管理器(Manager)
管理器用来管理session对象,Tomcat中定义了管理器接口:org.apache.catalina.Manager,同时提供了一个实现了管理器的公共方法的ManagerBase类,ManagerBase有两个子类:StandardManager和PersistentManagerBase。
在运行时,StandardManager把session保存在内存中,停止是会写入文件中,再次使用时又会从文件把这些session重新加载到内存当中,Manager的实现关系图如下:
其中DistributeManager只在Tomcat4中有。
Manager接口
Manager表示管理器组件,接口定义如下:
package org.apache.catalina;
import java.beans.PropertyChangeListener;
import java.io.IOException;
public interface Manager {
public Container getContainer();
public void setContainer(Container container);
public DefaultContext getDefaultContext();
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 createSession();
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;
}
首先,通过getContainer和setContainer方法可以把Manager分配个context容器,creatSession方法创建一个session对象,add方法把session添加到缓冲池中,remove方法把session从缓冲池中移除,getMaxInactiveInterval和setMaxInactiveInterval方法设置当用户没有操作会话以后管理器销毁session的最大间隔秒数。
其次,load和upload方法支持把session持久化到数据库磁盘等存储器中。
ManagerBase类
ManagerBase是一个抽象类,提供了对Manager接口的一些公共方法的实现。generateSessionId返回一个唯一的session标识。
所有的活动的session被缓存在Map中:
protected HashMap sessions = new HashMap();
向缓存map中添加移除session对象:
public void add(Session session) {
synchronized (sessions) {
sessions.put(session.getId(), session);
}
}
public void remove(Session session) {
synchronized (sessions) {
sessions.remove(session.getId());
}
}
查找相应session:
public Session findSession(String id) throws IOException {
if (id == null)
return (null);
synchronized (sessions) {
Session session = (Session) sessions.get(id);
return (session);
}
}
public Session[] findSessions() {
Session results[] = null;
synchronized (sessions) {
results = new Session[sessions.size()];
results = (Session[]) sessions.values().toArray(results);
}
return (results);
}
StandardManager类
StandardManager是Manager的标准实现类,会把session保存在内存中。它实现了LifeCycle接口,所以它可以启动和停止:
public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processExpires();
}
}
PersistentManagerBase类
PersistentManagerBase类是所有持久化Manager的父类。
Stores
Manager通过org.apache.catalina.Store接口来管理session存储,接口定义如下:
package org.apache.catalina;
import java.beans.PropertyChangeListener;
import java.io.IOException;
public interface Store {
public String getInfo();
public Manager getManager();
public void setManager(Manager manager);
public int getSize() throws IOException;
public void addPropertyChangeListener(PropertyChangeListener listener);
public String[] keys() throws IOException;
public Session load(String id)throws ClassNotFoundException, IOException;
public void remove(String id) throws IOException;
public void clear() throws IOException;
public void removePropertyChangeListener(PropertyChangeListener listener);
public void save(Session session) throws IOException;
}
最重要的是save和load方法,用来保存和加载session,有一个StoreBase类提供公共方法的实现,StoreBase类有两个类FileStore和JDBCStore,分别把session保存在文件和数据库中。