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保存在文件和數據庫中。