Chapter 9: Session Management
Overview(概述)
Catalina supports session management through a component called manager, which is represented by the org.apache.catalina.Manager interface. A manager is always associated with a context. Among others, a manager is responsible for creating, updating, and destroying (invalidating) session objects as well as returning a valid session object to any requesting component.
Catalina
通过一个称为 manager
的组件来支持会话管理,该组件由org.apache.catalina.Manager
接口表示。
一个manager总是与一个上下文相关联。
除其他功能外,manager
负责创建、更新和销毁(使无效)会话对象,以及向任何请求组件返回一个有效的会话对象。
A servlet can obtain a session object by calling the getSession method of the javax.servlet.http.HttpServletRequest interface, which is implemented by the org.apache.catalina.connector.HttpRequestBase class in the default connector. Here are some related methods in the HttpRequestBase class.
一个 servlet
可以通过调用javax.servlet.http.HttpServletRequest
接口的getSession
方法来获取一个session对象,该接口由默认连接器中的org.apache.catalina.connector.HttpRequestBase
类实现。
以下是HttpRequestBase
类中的一些相关方法。
public HttpSession getSession() {
return (getSession(true));
}
public HttpSession getSession(boolean create) {
...
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);
...
session = manager.createSession();
if (session != null)
return (session.getSession());
else return (null);
}
By default, a manager stores its session objects in memory. However, Tomcat also allows a manager to persist its session objects into a file store or a database (through JDBC). Catalina provides the org.apache.catalina.session package that contains types related to session objects and session management.
默认情况下,管理器将其 session
对象存储在内存中。
然而,Tomcat
还允许管理器将其 session
对象持久化到文件存储或数据库(通过JDBC)。
Catalina提供了org.apache.catalina.session
包,其中包含与 session 对象和session管理相关的类型。
This chapter explains the session management in Catalina in three sections, "Sessions", "Managers", and "Stores". The last section explains the application that uses a context with an associated Manager.
本章将以三个部分解释Catalina中的会话管理,即"会话"、"管理器"和"存储"。
最后一节解释了使用具有关联管理器的上下文的应用程序。
Sessions(会话)
In servlet programming, a session object is represented by the javax.servlet.http.HttpSession interface. The implementation of this interface is the StandardSession class in the org.apache.catalina.session package. However, for security reasons, a manager does not pass a StandardSession instance to a servlet. Instead, it uses a façade class StandardSessionFacade in the org.apache.catalina.session package. Internally, a manager works with another façade: the org.apache.catalina.Session interface. The UML diagram for the session-related types are given in Figure 9.1. Note that for brevity, the org.apache.catalina prefix on Session, StandardSession, and StandardSessionFacade has been omitted.
在 servlet
编程中,一个会话对象由 javax.servlet.http.HttpSession
接口表示。
这个接口的实现是org.apache.catalina.session
包中的StandardSession
类。
然而,出于安全原因,管理器不会将StandardSession
实例传递给servlet
,而是使用org.apache.catalina.session
包中的StandardSessionFacade
外观类。
在内部,管理器与另一个外观类一起工作:org.apache.catalina.Session
接口。
会话相关类型的UML图如图9.1所示。
请注意,为了简洁起见,省略了Session
、StandardSession
和StandardSessionFacade
上的org.apache.catalina
前缀。
Figure 9.1: Session-related types
图9.1:会话相关类型
The Session Interface(会话接口)
The Session interface acts as a Catalina-internal façade. The standard implementation of the Session interface, StandardSession, also implements the javax.servlet.http.HttpSession interface. The Session interface is given in Listing 9.1.
话接口充当Catalina内部的外观。会话接口的标准实现StandardSession
也实现了javax.servlet.http.HttpSession
接口。
会话接口如清单9.1所示。
Listing 9.1: The Session interface
清单 9.1:会话接口
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 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);
}
A Session object is always contained in a manager, and the setManager and getManager methods are used for associating a Session instance with a manager. A Session instance also has a unique identifier throughout the context associated with its manager. Session identifiers are accessed through the setId and getId methods. The getLastAccessedTime method is invoked by the manager to determine a Session object's validity. The manager calls the setValid method to set or reset a session's validity. Every time a Session instance is accessed, its access method is called to update its last accessed time. Finally, the manager can expire a session by calling its expire method and the getSession method returns an HttpSession object wrapped by this façade.
一个 Session
对象总是包含在一个管理器中,使用 setManager
和 getManager 方法将Session实例与管理器关联。
Session实例在与其管理器相关联的上下文中具有唯一的标识符。
通过 setId
和 getId
方法可以访问 Session
标识符。
getManager
调用 getLastAccessedTime
方法来确定 Session
对象的有效性。
管理器调用 setValid
方法来设置或重置会话的有效性。
每次访问 Session
实例时,都会调用其访问方法来更新其最后访问时间。
最后,管理器可以通过调用其 expire
方法来使会话过期,而 getSession
方法返回一个由此外观包装的 HttpSession
对象。
The StandardSession Class(标准会话类)
The StandardSession class is the standard implementation of the Session interface. In addition to implementing javax.servlet.http.HttpSession and org.apache.catalina.Session, StandardSession implements java.lang.Serializable to make Session objects serializable.
StandardSession
类是 Session
接口的标准实现。
除了实现javax.servlet.http.HttpSession
和org.apache.catalina.Session
之外,StandardSession
还实现了java.lang.Serializable,使得Session对象可序列化。
The constructor of this class accepts a Manager instance, forcing a Session object to always have a Manager.
该类的构造函数接受一个 Manager
实例,强制使Session对象始终具有一个 Manager
。
public StandardSession(Manager manager);
The following are some important private variables for maintaining states. Note that the transient keyword makes a variable non-serializable.
下面是一些用于维护状态的重要私有变量。
请注意,transient
关键字会使变量不可序列化。
// session attributes
private HashMap attributes = new HashMap();
// the authentication type used to authenticate our cached Principal,
if any
private transient String authType = null;
private long creationTime = 0L;
private transient boolean expiring = false;
private transient StandardSessionFacade facade = null;
private String id = null;
private long lastAccessedTime = creationTime;
// The session event listeners for this Session.
private transient ArrayList listeners = new ArrayList();
private Manager manager = null;
private int maxInactiveInterval = -1;
// Flag indicating whether this session is new or not.
private boolean isNew = false;
private boolean isValid = false;
private long thisAccessedTime = creationTime;
Note In Tomcat 5 the above variables are protected, in Tomcat 4 they are private. Each of these variables has an accessor and a mutator (the get/set methods).
注意 在 Tomcat 5 中,上述变量是受保护的,而在 Tomcat 4 中,它们是私有的。每个变量都有一个访问器和一个突变器(get/set 方法)。
The getSession method creates a StandardSessionFacade object by passing this instance:
getSession
方法通过传递此实例来创建 StandardSessionFacade
对象:
public HttpSession getSession() {
if (facade == null)
facade = new StandardSessionFacade(this);
return (facade);
}
A Session object that has not been accessed for a period of time exceeding the value of the maxInactiveInterval variable in the Manager will be made expired. Expiring a Session object is achieved by calling the expire method of the Session interface. The implementation of this method in StandardSession in Tomcat 4 is given in Listing 9.2.
会话对象在超过管理器中如果没有在 maxInactiveInterval
变量值的时间内被访问,会话对象将被视为过期。
会话对象的过期可通过调用会话接口的 expire
方法来实现。
Tomcat 4
中 StandardSession
中该方法的实现见清单 9.2。
Listing 9.2: The expire method
清单 9.2:过期方法
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();
}
}
The expiring process as shown in Listing 9.2 includes the setting of an internal variable called expiring, removing the Session instance from Manager, and firing of a few events.
如图9.2所示,列表9.2中的过期过程包括设置一个名为 expiring
的内部变量、从 Manager
中移除 Session
实例以及触发一些事件。
The StandardSessionFacade Class(标准会话面板类)
To pass a Session object to a servlet, Catalina could instantiate the StandardSession class, populate it, and then pass it to the servlet. However, instead, it passes it to an instance of StandardSessionFacade that provides only implementation of the methods in javax.servlet.http.HttpSession. This way, the servlet programmers cannot downcast the HttpSession object back to StandardSessionFacade and access its public methods that are not supposed to be available to the programmer.
为了将 Session
对象传递给 servlet
,Catalina
可以实例化 StandardSession
类,填充它,然后将其传递给 servlet
。
然而,它将其传递给 StandardSessionFacade
的一个实例,该实例仅提供 javax.servlet.http.HttpSession
中方法的实现。
这样,servlet
程序员无法将 HttpSession
对象向下转型为 StandardSessionFacade
并访问其不应该对程序员可用的公共方法。
Manager(管理器)
A manager manages session objects. For example, it creates session objects and invalidates them. A manager is represented by the org.apache.catalina.Manager interface. In Catalina, the org.apache.catalina.session package contains the ManagerBase class that provides implementation of common functionality. ManagerBase has two direct subclasses: StandardManager and PersistentManagerBase.
管理器用于管理会话对象。
例如,它创建会话对象并使其无效。
管理器由 org.apache.catalina.Manager
接口表示。
在 Catalina
中,org.apache.catalina.session
包包含提供常见功能实现的 ManagerBase
类。
ManagerBase
有两个直接子类:StandardManager
和 PersistentManagerBase
。
When running, StandardManager stores session objects in memory. However, when stopped, it saves all the session objects currently in memory to a file. When it is started again, it loads back these session objects.
在运行时,StandardManager
将会话对象存储在内存中。
然而,在停止时,它将当前在内存中的所有会话对象保存到文件中。
当再次启动时,它会加载这些会话对象。
PersistentManagerBase is a base class for manager components that store session objects in secondary storage. It has two sub-classes: PersistentManager and DistributedManager (DistributedManager is only available in Tomcat 4). The UML diagram of the Manager interface and its implementation classes is given in Figure 9.2.
PersistentManagerBase
是存储会话对象在辅助存储中的管理器组件的基类。
它有两个子类:PersistentManager
和 DistributedManager
(DistributedManager
仅在Tomcat 4中可用)。
Manager
接口及其实现类的UML图如图9.2所示。
Figure 9.2: The Manager interface and its implementations
图 9.2:管理器接口及其实现
The Manager Interface(管理器界面)
The Manager interface represents a Manager component. It is given in Listing 9.3.
管理器接口表示一个管理器组件。
它在清单 9.3 中给出。
Listing 9.3: The Manager interface
清单 9.3:管理器接口
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;
}
First of all, the Manager interface has the getContainer and setContainer methods to associate a Manager implementation with a context. The createSession method creates a Session object. The add method adds a Session instance to the session pool and the remove method removes the Session object from the pool. The getMaxInactiveInterval and the setMaxInactiveInterval methods return and specifies the number of seconds the Manager will wait for the user associated with a session to come back before destroying the session.
首先,管理器接口有 getContainer
和 setContainer
方法,用于将管理器实现与上下文关联起来。
createSession
方法创建会话对象。
add
方法会将会话实例添加到会话池中,remove
方法会将会话对象从池中移除。
getMaxInactiveInterval
和 setMaxInactiveInterval
方法返回并指定管理器在销毁会话前等待会话关联用户返回的秒数。
Finally, the load and unload methods are there to support persisting sessions to a secondary storage in a Manager implementation that supports a kind of persistence mechanism. The unload method saves currently active sessions to a store specified by the Manager implementation, and the load method brings back the sessions persisted to the memory.
最后,在支持某种持久化机制的管理器实现中,加载和卸载方法用于支持将会话持久化到二级存储中。
卸载方法会将当前活动会话保存到管理器实现指定的存储空间中,而加载方法则会将持久化到内存中的会话带回来。
The ManagerBase Class(管理器基类)
The ManagerBase class is an abstract class from which all Manager classes derive. This class provides common functionality for its child classes. Among others, ManagerBase has the createSession method for creating a Session object. Each session has a unique identifier, and the ManagerBase class's protected method generateSessionId returns a unique identifier.
ManagerBase
类是一个抽象类,所有 Manager
类都是从它派生出来的。该类为其子类提供通用功能。
其中,ManagerBase
具有 createSession
方法,用于创建会话对象。
每个会话都有一个唯一标识符,ManagerBase
类的受保护方法 generateSessionId
会返回一个唯一标识符。
Note An active session is a session object that is still valid (has not expired)
注意 活动会话是指仍然有效(未过期)的会话对象
The instance of Manager for a given context manages all active sessions in the context. These active sessions are stored in a HashMap called sessions:
给定上下文的 Manager
实例管理该上下文中的所有活动会话。
这些活动会话存储在名为 sessions
的 HashMap
中:
protected HashMap sessions = new HashMap();
The add method adds a Session object to the sessions HashMap. This method is given below.
add 方法将一个会话对象添加到会话 HashMap
中。
该方法如下所示。
public void add(Session session) {
synchronized (sessions) {
sessions.put(session.getId(), session);
}
}
The remove method removes a Session from the sessions HashMap. Here is the remove method.
remove
方法会从 sessions
HashMap
中移除一个 Session
。下面是 remove
方法。
public void remove(Session session) {
synchronized (sessions) {
sessions.remove(session.getId());
}
}
The no-argument findSession method returns all active sessions from the sessions HashMap as an array of Session instances. The findSession method that accepts a session identifier as an argument returns the Session instance with that identifier. These method overloads are given below.
无参数 findSession
方法以会话实例数组的形式返回会话 HashMap
中的所有活动会话。
findSession
方法接受会话标识符作为参数,返回具有该标识符的会话实例。
下面给出了这些方法的重载。
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
The StandardManager class is the standard implementation of Manager and stores session objects in memory. It implements the Lifecycle interface (See Chapter 6, "Lifecycles") so that it can be started and stopped. The implementation of the stop method calls the unload method that serializes valid Session instances to a file called SESSIONS.ser for each context. The SESSIONS.ser file can be found under the work directory under CATALINA_HOME. For example, in Tomcat 4 and 5, if you have run the examples application, you can find a SESSIONS.ser file under CATALINA_HOME/work/Standalone/localhost/examples. When StandardManager is started again, these Session objects are read back to memory by calling the load method.
StandardManager
类是 Manager
的标准实现,它将会话对象存储在内存中。
它实现了 Lifecycle
接口(见第 6 章“生命周期”),因此可以启动和停止。
停止方法的实现调用 unload
方法,将有效的 Session
实例序列化到名为 SESSIONS.ser
的文件中,每个上下文一个文件。
SESSIONS.ser 文件可以在 CATALINA_HOME 的 work 目录下找到。
例如,在 Tomcat 4 和 5 中,如果您运行了示例应用程序,可以在 CATALINA_HOME/work/Standalone/localhost/examples
下找到一个 SESSIONS.ser
文件。
当 StandardManager
再次启动时,通过调用 load
方法,这些 Session
对象将被读回到内存中。
A manager is also responsible for destroying session objects that are no longer valid. In StandardManager in Tomcat 4, this is achieved by employing a dedicated thread. For this reason, StandardManager implements java.lang.Runnable. Listing 9.4 presents the run method of the StandardManager class in Tomcat 4
Manager
也负责销毁不再有效的会话对象。
在 Tomcat 4 的 StandardManager
中,这是通过使用专用线程来实现的。
因此,StandardManager
实现了 java.lang.Runnable
。清单 9.4 展示了 Tomcat 4 中 StandardManager
类的 run
方法。
Listing 9.4: The run method of StandardManager in Tomcat 4
清单 9.4: Tomcat 4 中 StandardManager
的运行方法
public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processExpires();
}
}
The threadSleep method puts the thread to sleep for the number of seconds specified by the checkInterval variable, which by default has a value of 60. You can change this value by calling the setCheckInterval method.
threadSleep
方法会将线程休眠指定的秒数,该秒数由 checkInterval
变量指定,默认值为 60
。您可以通过调用 setCheckInterval
方法来更改该值。
The processExpire method loops through all Session objects managed by StandardManager and compares each Session instance's lastAccessedTime with the current time. If the difference between the two exceeds maxInactiveInterval, the method calls the Session interface's expire method to expire the Session instance. The value of maxInactiveInternal can be changed by calling the setMaxInactiveInterval method. The default value of maxInactiveInterval variable in StandardManager is 60. Don't be fooled into thinking that this is the value used in a Tomcat deployment, though. The setContainer method, which is called by the setManager method in the org.apache.catalina.core.ContainerBase class (you always call setManager to associate a manager with a context), overwrites this value. Here is the piece of code in the setContainer method:
processExpire
方法会循环遍历 StandardManager
管理的所有 Session
对象,并将每个 Session
实例的 lastAccessedTime
与当前时间进行比较。
如果两者之间的差值超过 maxInactiveInterval
,该方法将调用 Session
接口的 expire
方法来使 Session
实例过期。
可以通过调用 setMaxInactiveInterval
方法来更改 maxInactiveInterval
的值。
在 StandardManager
中,maxInactiveInterval
变量的默认值为 60
。
但请不要被误导以为这是Tomcat部署中使用的值。
实际上,org.apache.catalina.core.ContainerBase
类中的 setContainer
方法会覆盖这个值。以下是 setContainer
方法中的代码片段:
setMaxInactiveInterval(((Context)
this.container).getSessionTimeout()*60 );
Note The default value of the sessionTimeOut variable in the org.apache.catalina.core.StandardContext class is 30.
注意 org.apache.catalina.core.StandardContext 类中 sessionTimeOut 变量的默认值是 30。
In Tomcat 5, the StandardManager class does not implement java.lang.Runnable. The processExpires method in a StandardManager object in Tomcat 5 is directly called by the backgroundprocess method, which is not available in Tomcat 4.
在 Tomcat 5 中,StandardManager
类没有实现 java.lang.Runnable
。
在 Tomcat 5 中,StandardManager
对象中的 processExpires
方法是由 backgroundprocess
方法直接调用的,而在 Tomcat 4 中则不可用。
public void backgroundProcess() {
processExpires();
}
The backgroundProcess method in StandardManager is invoked by the backgroundProcess method of the org.apache.catalina.core.StandardContext instance, the container associated with this manager. StandardContext invokes its backgroundProcess method periodically and will be discussed in Chapter 12
StandardManager
中的 backgroundProcess
方法由与该管理器关联的容器org.apache.catalina.core.StandardContext
实例的 backgroundProcess
方法调用。
StandardContext
会定期调用其 backgroundProcess
方法,这将在第 12 章中讨论。
PersistentManagerBase
The PersistentManagerBase class is the parent class of all persistent managers. The main difference between a StandardManager and a persistent manager is the presence of a store in the latter. A store represents a secondary storage for the managed session objects. The PersistentManagerBase class uses a private object reference called store.
PersistentManagerBase
类是所有持久化管理器的父类。
StandardManager
与持久化管理器的主要区别在于后者有一个存储空间。
存储空间代表被管理会话对象的二级存储空间。
PersistentManagerBase
类使用名为 store
的私有对象引用。
private Store store = null;
In a persistent manager, session objects can be backed up as well as swapped out. When a session object is backed up, the session object is copied into a store and the original stays in memory. Therefore, if the server crashes, the active session objects can be retrieved from the store. When a session object is swapped out, it is moved to the store because the number of active session objects exceeds a specified number or the session object has been idle for too long. The purpose of swapping out is to save memory.
在持久化管理器中,会话对象可以被备份和交换出去。
当一个会话对象被备份时,该会话对象被复制到存储中,而原始对象仍然留在内存中。
因此,如果服务器崩溃,活动会话对象可以从存储中检索出来。
当一个会话对象被交换出去时,它被移动到存储中,因为活动会话对象的数量超过了指定的数量,或者会话对象已经闲置了太长时间。
交换出的目的是为了节省内存。
In Tomcat 4 PersistentManagerBase implements java.lang.Runnable to employ a separate thread that routinely backs up and swaps out active sessions. Here is its run method implementation:
在Tomcat 4
中,PersistentManagerBase
实现了java.lang.Runnable
,以利用一个单独的线程定期备份和交换出活动会话。以下是其 run
方法的实现:
public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processExpires();
processPersistenceChecks();
}
}
The processExpired method, just like in StandardManager, checks for expiring session objects. The processPersistenceChecks method calls three other methods:
processExpired
方法与 StandardManager
中的一样,用于检查过期的会话对象。
processPersistenceChecks
方法会调用其他三个方法:
public void processPersistenceChecks() {
processMaxIdleSwaps();
processMaxActiveSwaps();
processMaxIdleBackups();
}
In Tomcat 5 PersistentManagerBase does not implement java.lang.Runnable. Backing up and swapping out is done by its backgroundProcess manager, which is periodically invoked by the associated StandardContext instance.
在 Tomcat 5 中,PersistentManagerBase
没有实现 java.lang.Runnable
。
备份和换出由其后台进程管理器(backgroundProcess manager
)完成,该进程由相关的 StandardContext
实例定期调用。
Swap out and back up are discussed in the following sub-sections.
交换和备份将在下面的小节中讨论。
Swap Out(交换)
The PersistentManagerBase class applies a number of rules in swapping out session objects. A session object is swapped out either because the number of active sessions has exceeded the value of maxActiveSessions variable or because the session has been idle for too long.
PersistentManagerBase
类在交换会话对象时应用了一些规则。
会话对象之所以被交换出,要么是因为活动会话的数量超过了 maxActiveSessions
变量的值,要么是因为会话已经闲置了太长时间。
In the case where there are too many session objects, a PersistentManagerBase instance simply swaps out any session objects until the number of active session objects is equal to maxActiveSessions. (See the processMaxActiveSwaps method)
在会话对象过多的情况下,PersistentManagerBase
实例会简单地交换出任何会话对象,直到活动会话对象的数量等于 maxActiveSessions
。(参见 processMaxActiveSwaps
方法)
In the case where a session object has been idle for too long, the PersistentManagerBase class uses two variables to determine whether or not a session object should be swapped out: minIdleSwap and maxIdleSwap. A session object will be swapped out if its lastAccessedTime is exceeds both minIdleSwap and maxIdleSwap. To prevent any swapping out, you can set the maxIdleSwap to a negative number. (See the processMaxIdleSwaps method)
在会话对象闲置时间过长的情况下,PersistentManagerBase
类使用两个变量来确定是否应该交换出会话对象:minIdleSwap
和 maxIdleSwap
。
如果会话对象的 lastAccessedTime
超过了 minIdleSwap
和 maxIdleSwap
的值,会话对象将被交换出。
如果要防止任何交换出,可以将 maxIdleSwap
设置为负数。
(参见 processMaxIdleSwaps
方法)
Because an active session can be swapped out, it can either reside in memory or in a store. Therefore, the findSession (String id) method first looks for the Session instance in the memory and, if none is found, in the store. Here is the method implementation in the PersistentManagerBase class.
由于活动会话可以被交换出,它可以驻留在内存中或者存储中。
因此,findSession
(String id
)方法首先在内存中查找 Session
实例,如果找不到,则在存储中查找。
下面是 PersistentManagerBase
类中该方法的实现。
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
session = swapIn(id); // swapIn returns an active session in the
// Store return (session);
}
Back-up(备份)
Not all active session objects are backed up. A PersistentManagerBase instance only backs up session objects that have been idle longer than the value of maxIdleBackup. The processMaxIdleBackups method performs the session object back-up.
并非所有活动的会话对象都会被备份。
PersistentManagerBase
实例只会备份那些闲置时间超过 maxIdleBackup
值的会话对象。
processMaxIdleBackups
方法用于执行会话对象的备份。
PersistentManager(持久化管理器)
The PersistentManager class extends PersistentManagerBase. There is not much addition here, except two properties. The PersistentManager class is given in Listing 9.5.
PersistentManager
类继承自 PersistentManagerBase
。
除了两个属性外,这里没有太多的添加。 PersistentManager
类的代码如下所示:
Listing 9.5: The PersistentManager class
代码清单9.5:PersistentManager
类
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);
}
}
DistributedManager(分布式管理器)
Tomcat 4 provides the DistrubutedManager class. A subclass of PersistentManagerBase, DistributedManager is used in a clustered environment with two or more nodes. A node represents a Tomcat deployment. Nodes in a cluster can exist in different machines or the same machine. In a clustered environment, each node must use an instance of DistributedManager as its Manager to support session replication, which is the main responsibility of DistributedManager.
Tomcat 4提供了 DistributedManager
类。DistributedManager
是 PersistentManagerBase
的子类,用于在具有两个或多个节点的集群环境中使用。
一个节点代表一个Tomcat部署。集群中的节点可以存在于不同的机器或同一台机器上。
在集群环境中,每个节点必须使用 DistributedManager
的一个实例作为其 Manager
来支持会话复制,这是 DistributedManager
的主要职责。
For the replication purpose, DistributedManager sends notification to other nodes whenever a session object is created or destroyed. In addition, a node must be able to receive notification from other nodes as well. This way, an HTTP request can be served by any node in the cluster.
为了复制目的,每当会话对象被创建或销毁时,DistributedManager
会向其他节点发送通知。
此外,一个节点必须能够接收来自其他节点的通知。
这样,一个HTTP请求可以由集群中的任何节点来提供服务。
For sending and receiving notification to and from other instances of DistributedManager in other nodes, Catalina provides classes in the org.apache.catalina.cluster package. Among others, the ClusterSender class is used for sending notifications to other nodes and the ClusterReceiver class for receiving notifications from other nodes.
为了与其他节点中的 DistributedManager
实例发送和接收通知,Catalina
提供了 org.apache.catalina.cluster
包中的类。
除其他外,ClusterSender
类用于向其他节点发送通知,ClusterReceiver类用于从其他节点接收通知。
The createSession method of DistrbutedManager must create a session object to be stored in the current instance and use a ClusterSender instance to send notification to other nodes. Listing 9.6 presents the createSession method.
DistributedManager
的 createSession
方法必须创建一个会话对象以存储在当前实例中,并使用 ClusterSender
实例向其他节点发送通知。
以下是 createSession
方法的示例代码:
Listing 9.6: The createSession method
示例代码9.6: createSession方法
public Session createSession() {
Session session = super.createSession();
ObjectOutputStream oos = null;
ByteArrayOutputStream bos = null;
ByteArraylnputStream bis = null;
try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(new BufferedOutputStream(bos));
((StandardSession)session).writeObjectData(oos);
oos.close();
byte[] obs = bos.toByteArray();
clusterSender.send(obs);
if(debug > 0)
log("Replicating Session: "+session.getId());
}
catch (IOException e) {
log("An error occurred when replicating Session: " +
session.getId());
}
retun (session);
}
First, the createSession method calls the super class's createSession method to create a session object for itself. Then, it sends the session object as an array of bytes using the ClusterSender.
首先,createSession
方法调用其超类的 createSession
方法为自身创建一个会话对象。
然后,它使用 ClusterSender
将会话对象作为字节数组发送出去。
The DistribubedManager class also implements java.lang.Runnable to have a separate thread for expiring session objects and receive notification from other nodes. The run method is as follows.
DistribubedManager
类还实现了 java.lang.Runnable
接口,以便为过期的会话对象和来自其他节点的通知接收单独的线程。
run
方法如下所示。
public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processClusterReceiver();
processExpires();
processPersistenceChecks();
}
}
What's worth noting in this method is the call to the processClusterReceiver method that processes session create notification from other nodes.
该方法中值得注意的是对 processClusterReceiver
方法的调用,该方法用于处理来自其他节点的会话创建通知。
Stores(存储器)
A store, represented by the org.apache.catalina.Store interface, is a component that provides a permanent storage for sessions managed by a manager. The Store interface is given in Listing 9.7.
存储由 org.apache.catalina.Store
接口表示,是为管理器管理的会话提供永久存储的组件。
清单 9.7 给出了存储接口。
Listing 9.7: The Store interface
清单 9.7:存储接口
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;
pubiic void removePropertyChangeListener(PropertyChangeListener
listener);
public void save(Session session) throws IOException;
}
The two most important methods in the Store interface are save and load. The save method saves the specified session object to a permanent storage. The load method loads the session object with a given session identifier from the storage. Also, the keys method returns a String array containing all session identifiers.
在Store接口中,最重要的两个方法是 save
和 load
。
save
方法将指定的会话对象保存到永久存储中。
load
方法从存储中加载具有给定会话标识符的会话对象。
另外,keys
方法返回一个包含所有会话标识符的字符串数组。
The UML diagram for the Store interface and its implementation classes is given in Figure 9.3. Note that the org.apache.catalina prefix has been omitted from all type names.
Store
接口及其实现类的UML
图如图9.3所示。
请注意,所有类型名称中都省略了org.apache.catalina
前缀。
Figure 9.3: The Store interface and its implementations
图 9.3:存储接口及其实现
The following sub-sections discuss the StoreBase, FileStore, and JDBCStore classes.
下面的小节将讨论 StoreBase
、FileStore
和 JDBCStore
类。
StoreBase(存储库)
The StoreBase class is an abstract class that provides common functionality for the two child classes: FileStore and JDBCStore. The StoreBase class does not implement the Store interface's save and load methods because the implementations of these methods depend on the type of storage to persist sessions to.
StoreBase
类是一个抽象类,为两个子类提供通用功能: FileStore
和 JDBCStore
。
StoreBase
类没有实现存储接口的保存和加载方法,因为这些方法的实现取决于会话持久化的存储类型。
The StoreBase class in Tomcat 4 employs a separate thread to regularly check for expired session and remove expired sessions from the collection of active sessions. Here is the run method in StoreBase in Tomcat 4.
Tomcat 4 中的 StoreBase
类使用一个单独的线程定期检查过期会话,并从活动会话集合中删除过期会话。
下面是 Tomcat 4 中 StoreBase
的运行方法。
public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processExpires();
}
}
The processExpires method retrieves all active sessions and checks the lastAccessedTime value of each session, and removes the session objects that have been inactive for too long. The processExpires method is given in Listing 9.7
processExpires
方法检索所有活动会话,并检查每个会话的 lastAccessedTime
值,并删除那些长时间不活动的会话对象。
processExpires
方法在清单 9.7 中给出。
Listing 9.7: the processExpires method
清单 9.7:processExpires
方法
protected void processExpires() {
long timeNow = System.currentTimeMillis();
String[] keys = null;
if (!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.isValid())
continue;
int maxInactiveInterval = session.getMaxInactiveInterval();
if (maxInactiveInterval < 0)
continue;
int timeIdle = // Truncate, do not round up
(int) ((timeNow - session.getLastAccessedTime()) / 1000L);
if (timeIdle >= maxInactiveInterval) {
if ( ( (PersistentManagerBase) manager).isLoaded( keys[i] )) {
// recycle old backup session
session.recycle();
}
else {
// expire swapped out session
session.expire();
}
remove(session.getId());
}
}
catch (IOException e) {
log (e.toString());
e.printStackTrace();
}
catch (ClassNotFoundException e) {
log (e.toString());
e.printStackTrace();
}
}
}
In Tomcat 5 there is no special thread for calling the processExpires method. Instead, this method is periodically called by the backgroundProcess method of the associated PersistentManagerBase instance.
在Tomcat 5
中,没有专门的线程来调用 processExpires
方法。
相反,这个方法是由关联的 PersistentManagerBase
实例的 backgroundProcess
方法定期调用的。
FileStore
The Filestore class stores session objects in a file. The file is named the same as the identifier of the session object plus the extension .session. This file resides in the temporary work directory. You can change the temporary directory by calling the Filestore class's setDirectory
FileStore类将会话对象存储在文件中。文件的名称与会话对象的标识符相同,加上扩展名.session
。
这个文件位于临时工作目录中。您可以通过调用FileStore类的setDirectory方法来更改临时目录。
The java.io.ObjectOutputStream class is used in the save method to serialize the session objects. Therefore, all objects stored in a Session instance must implement java.lang.Serializable. To deserialize a session object in the load method, the java.io.ObjectInputStream class is used.
在save方法中,使用 java.io.ObjectOutputStream
类来序列化会话对象。
因此,存储在Session实例中的所有对象都必须实现java.lang.Serializable
接口。
在 load
方法中,使用java.io.ObjectInputStream
类来反序列化会话对象。
JDBCStore
The JDBCStore class stores session objects in a database and transfers are done through JDBC. As such, to use JDBCStore you need to set the driver name and the connection URL by calling the setDriverName and setConnectionURL respectively.
JDBCStore
类将会话对象存储在数据库中,并通过 JDBC
进行传输。
因此,要使用 JDBCStore
,需要分别调用 setDriverName
和 setConnectionURL
来设置驱动程序名称和连接 URL。
The Application
The application accompanying this article is similar to the one in Chapter 8. It uses the default connector and has a context as its main container with one wrapper. One difference, however, is that the context in this application has a StandardManager instance to manage session objects. To test this application, you use the third example servlet SessionServlet. This servlet is represented by a wrapper named wrapper1.
本文附带的应用程序与第8章中的应用程序类似。
它使用默认连接器,并将上下文作为主容器,其中包含一个包装器。
然而,与之不同的是,该应用程序中的上下文具有一个StandardManager
实例来管理会话对象。
要测试此应用程序,您可以使用第三个示例servlet SessionServlet
。
此servlet由名为wrapper1
的包装器表示。
Note You can find the SessionServlet class under the myApp/WEB-INF/classes directory.
注意 您可以在myApp/WEB-INF/classes目录下找到SessionServlet类。
The application has two packages, ex09.pyrmont.core and ex09.pyrmont.startup, and uses various classes in Catalina. There are four classes in the ex09.pyrmont.core package: SimpleContextConfig, SimplePipeline, SimpleWrapper, and SimpleWrapperValve. The first three classes are simply copies of the same classes in Chapter 8. There are, however, two additional lines of code in the SimpleWrapperValve class. The ex09.pyrmont.startup package has one class: Bootstrap.
该应用程序有两个包,ex09.pyrmont.core和ex09.pyrmont.startup
,并使用Catalina中的各种类。ex09.pyrmont.core
包中有四个类:SimpleContextConfig
、SimplePipeline
、SimpleWrapper
和 SimpleWrapperValve
。
前三个类只是第8章中相同类的副本。
然而,SimpleWrapperValve
类中有两行额外的代码。
ex09.pyrmont.startup
包中有一个类:Bootstrap
。
The Bootstrap class is explained in the first sub-section of this section and the SimpleWrapperValve class in the second sub-section. The last subsection discusses how to run the application.
Bootstrap
类在本节的第一个小节中进行了解释,SimpleWrapperValve
类在第二个小节中进行了解释。
最后一个小节讨论了如何运行该应用程序。
The Bootstrap Class
The Bootstrap class starts the application and it is very similar to the Bootstrap class in Chapter 8. However, the Bootstrap class in this chapter creates an instance of the org.apache.catalina.session.StandardManager class and associate it with the Context.
引导类启动应用程序,与第8章中的引导类非常相似。
然而,本章中的引导类创建了一个 org.apache.catalina.session.StandardManager
类的实例,并将其与上下文关联起来。
The main method starts by setting the catalina.base system property and instantiating the default connector
主方法首先设置 catalina.base 系统属性,然后实例化默认连接器。
System.setProperty("catalina.base",
System.getProperty("user.dir"));
Connector connector = new HttpConnector();
For the SessionServlet, it creates a wrapper called wrapper1
对于 SessionServlet
,它会创建一个名为 wrapper1 的封装器
Wrapper wrapper1 = new SimpleWrapper();
wrapper1.setName("Session");
wrapper1.setServletclass("SessionServlet");
Then, it creates a StandardContext object, sets its path and docBase properties, and adds the wrapper to the context.
然后,它会创建一个 StandardContext 对象,设置其路径和 docBase 属性,并将包装器添加到上下文中。
Context context = new StandardContext();
context.setPath("/myApp");
context.setDocBase("myApp");
context.addChild(wrapper1);
Next, the start method continues by adding a servlet mapping. The mapping is different from the ones in the application in Chapter 8. Instead of /Session, we use /myApp/Session as the pattern. This is required because we set the path name of the context to /myApp. The path name will be used to send the session cookie, and the browser will only send the cookie back to the server if the path is also /myApp.
接下来,start
方法继续添加一个 servlet
映射。
这个映射与第8章中的应用程序中的映射不同。
我们使用/myApp/Session
作为模式,而不是/Session
。
这是必需的,因为我们将上下文的路径名称设置为/myApp
。
路径名称将用于发送会话 cookie
,只有当路径也为/myApp
时,浏览器才会将cookie
发送回服务器。
context.addServletMapping("/myApp/Session", "Session");
The URL used to request SessionServlet is as follows.
用于请求 SessionServlet
的 URL 如下。
http://localhost:8080/myApp/Session.
Just like in Chapter 8, we need a listener and a loader for the context.
与第 8 章中一样,我们需要为上下文设置一个监听器和一个加载器。
LifecycleListener listener = new SimpleContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
// here is our loader
Loader loader = new WebappLoader();
// associate the loader with the Context
context.setLoader(loader);
connector.setContainer (context);
Now, new to this application is a manager. We use an instance of StandardManager and pass it to the context.
现在,管理器是本应用程序的新成员。我们使用 StandardManager
的一个实例,并将其传递给上下文。
Manager manager = new StandardManager();
context.setManager(manager);
Finally, we initialize and start the connector and start the context.
最后,我们初始化和启动连接器,并启动上下文。
connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) context).start();
The SimpleWrapperValve Class
Recall that at the beginning of this chapter, we mentioned that the servlet can get a session object by calling the getSession method of the javax.servlet.http.HttpServletRequest interface. When the getSession method is invoked, the request object must somehow call the manager associated with the context. The manager either creates a new session object or returns the existing one. For the request object to be able to access the manager, it must have access to the context. To achieve this, in the invoke method of the SimpleWrapperValve class, you call the setContext method of the org.apache.catalina.Request interface, passing the Context. Remember that the SimpleWrapperValve class's invoke method calls the requested servlet's service method. Therefore, you must set the Context before the servlet's service method is called. The invoke method of the SimpleWrapperValve class is given in Listing 9.8. The highlighted lines are the addition to this class.
回想一下,在本章的开头,我们提到servlet
可以通过调用javax.servlet.http.HttpServletRequest
接口的 getSession
方法来获取一个会话对象。
当调用 getSession
方法时,请求对象必须以某种方式调用与上下文相关联的管理器。
管理器要么创建一个新的会话对象,要么返回现有的会话对象。
为了使请求对象能够访问管理器,它必须能够访问上下文。
为了实现这一点,在 SimpleWrapperValve
类的 invoke
方法中,您需要调用 org.apache.catalina.Request
接口的setContext
方法,将上下文传递给它。
请记住,SimpleWrapperValve
类的 invoke
方法调用了所请求的 servlet
的 service
方法。
因此,在调用 servlet
的 service
方法之前,您必须设置上下文。
SimpleWrapperValve
类的 invoke
方法如9.8节所示。
这些突出显示的行是对该类的添加内容。
Listing 9.8: The invoke method of the SimpleWrapperValve class
清单 9.8:SimpleWrapperValve
类的 invoke
方法
public void invoke(Request request, Response response,
ValveContext valveContext) throws IOException, ServletException {
SimpleWrapper wrapper = (SimpleWrapper) getContainer();
ServletRequest sreq = request.getRequest();
ServletResponse sres = response.getResponse();
Servlet servlet = null;
HttpServletRequest hreq = null;
if (sreq instanceof HttpServletRequest)
hreq = (HttpServletRequest) sreq;
HttpServletResponse hres = null;
if (sres instanceof HttpServletResponse)
hres = (HttpServletResponse) sres;
// pass the Context to the Request object so that
// the Request object can call the Manager
Context context = (context) wrapper.getparent();
request.setcontext(context);
// Allocate a servlet instance to process this request
try {
servlet = wrapper.allocate();
if (hres!=null && hreq!=null) {
servlet.service(hreq, hres);
}
else {
servlet.service(sreq, sres);
}
}
catch (ServletException e) {
}
}
You have access to the wrapper, therefore you can obtain the context by calling the getParent method of the Container interface. Note that the wrapper was added to a context. Once you have the context, you can then call the setContext method of the Request interface.
你可以通过调用 Container
接口的 getParent
方法,获得对包装器的访问权限,从而获取上下文。
注意,包装器已添加到上下文中。
一旦获取到上下文,你可以调用 Request
接口的 setContext
方法。
As explained at the beginning of the chapter, the private doGetSession method in the org.apache.catalina.connector.HttpRequestBase class can then call the getManager method of the Context interface to obtain the manager.
正如在本章开头解释的那样,org.apache.catalina.connector.HttpRequestBase
类中的私有方法 doGetSession
可以调用 Context
接口的 getManager
方法来获取管理器。
// Return the requested session if it exists and is valid
Manager manager = null;
if (context != null)
manager = context.getManager();
Once you have the manager, obtaining a session object or creating a new one is straightforward.
有了管理器后,获取会话对象或创建新的会话对象就很简单了。
Running the Application
To run the application in Windows, from the working directory, type the following:
要在 Windows
中运行应用程序,请在工作目录中键入以下内容:
java -classpath ./lib/servlet.jar;./lib/commons-collections.jar;./
ex09.pyrmont.startup.Bootstrap
In Linux, you use a colon to separate between libraries.
在 Linux 中,库与库之间用冒号分隔。
java -classpath ./lib/servlet.jar:./lib/commons-collection.jar:./
ex09.pymont.startup.Bootstap
To invoke the SessionServlet servlet, you use the following URL in your browser.
要调用 SessionServlet
服务程序,可在浏览器中使用以下 URL。
http://localhost:8080/myApp/Session
SessionServlet uses a session object to store a value. This servlet displays the previous value and the current value in the Session object, It also displays a form that the user can use to enter a new value, Figure 9.4 displays the output from SessionServlet when it is first invoked.
SessionServlet
使用会话对象来存储一个数值。
该 Servlet
显示了会话对象中的先前数值和当前数值。
它还显示了一个表单,用户可以用来输入新数值。图9.4显示了当首次调用 SessionServlet
时的输出。
Figure 9.4: The output from SessionServlet
图 9.4: SessionServlet 的输出
Summary
This chapter discussed the manager, the component that manages sessions in the session management. It explained the types of managers and how a manager can persist session objects into a store. At the end of the chapter, you have learned how to build an application that uses a StandardManager instance to run a servlet that uses session objects to store values.
本章讨论了会话管理中管理会话的组件--管理器。
它解释了管理器的类型以及管理器如何将会话对象持久化到存储中。
在本章结束时,你已学会如何构建一个应用程序,使用 StandardManager
实例运行一个使用会话对象存储值的 servlet
。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。