我终于设法解决了这个问题:
- 我扩展
org.apache.catalina.session.ManagerBase
了覆盖使用超类会话映射的每个方法,以便它直接攻击文件(或缓存)。
例子:
@Override
public HashMap<String, String> getSession(String sessionId) {
Session s = getSessionFromStore(sessionId);
if (s == null) {
if (log.isInfoEnabled()) {
log.info("Session not found " + sessionId);
}
return null;
}
Enumeration<String> ee = s.getSession().getAttributeNames();
if (ee == null || !ee.hasMoreElements()) {
return null;
}
HashMap<String, String> map = new HashMap<>();
while (ee.hasMoreElements()) {
String attrName = ee.nextElement();
map.put(attrName, getSessionAttribute(sessionId, attrName));
}
return map;
}
重要:
load 和 unload 方法必须留空:
@Override
public void load() throws ClassNotFoundException, IOException {
// TODO Auto-generated method stub
}
@Override
public void unload() throws IOException {
// TODO Auto-generated method stub
}
您必须覆盖 startInternal 和 stopInternal 以防止生命周期错误:
@Override
protected synchronized void startInternal() throws LifecycleException {
super.startInternal();
// Load unloaded sessions, if any
try {
load();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("standardManager.managerLoad"), t);
}
setState(LifecycleState.STARTING);
}
@Override
protected synchronized void stopInternal() throws LifecycleException {
if (log.isDebugEnabled()) {
log.debug("Stopping");
}
setState(LifecycleState.STOPPING);
// Write out sessions
try {
unload();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("standardManager.managerUnload"), t);
}
// Expire all active sessions
Session sessions[] = findSessions();
for (int i = 0; i < sessions.length; i++) {
Session session = sessions[i];
try {
if (session.isValid()) {
session.expire();
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
} finally {
// Measure against memory leaking if references to the session
// object are kept in a shared field somewhere
session.recycle();
}
}
// Require a new random number generator if we are restarted
super.stopInternal();
}
- 以上允许始终从文件(或缓存)中读取,但写操作呢?为此,我扩展了
org.apache.catalina.session.StandardSession
覆盖public void setAttribute(String name, Object value, boolean notify)
和public void removeAttribute(String name, boolean notify)
.
例子:
@Override
public void setAttribute(String name, Object value, boolean notify) {
super.setAttribute(name, value, notify);
((DataGridManager)this.getManager()).getCacheManager().getCache("sessions").put(this.getIdInternal(), this);
}
@Override
public void removeAttribute(String name, boolean notify) {
super.removeAttribute(name, notify);
((DataGridManager)this.getManager()).getCacheManager().getCache("sessions").put(this.getIdInternal(), this);
}
重要的:
在我们的例子中,真正的会话备份最终成为一个缓存(不是文件),当我们从中读取扩展的 Tomcat 会话时(在我们的 ManagerBase impl 类中),我们不得不以一种丑陋的方式对其进行调整,以便一切正常:
private Session getSessionFromStore(String sessionId){
DataGridSession s = (DataGridSession)cacheManager.getCache("sessions").get(sessionId);
if(s!=null){
try {
Field notesField;
notesField = StandardSession.class.getDeclaredField("notes");
notesField.setAccessible(true);
notesField.set(s, new HashMap<String, Object>());
s.setManager(this);
} catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
throw new RuntimeException(e);
}
}
return s;
}