345290 Weak references from SessionIdManager. HashSessionManager cleanup.
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@3123 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
parent
70d0d0d1f2
commit
50ebfdd13b
|
@ -18,6 +18,7 @@ jetty-7.4.1-SNAPSHOT
|
|||
+ 344513 Attempting to set ConfigurationClasses in jetty-web.xml causes NPE
|
||||
+ 344529 Ability to customize the error handling of the OSGi HttpService
|
||||
+ 345047 Readded deprecated ScanningAppDeployer#setMonitoredDir
|
||||
+ 345290 Weak references from SessionIdManager. HashSessionManager cleanup.
|
||||
+ JETTY-954 WebAppContext eats any start exceptions instead of stopping the server load
|
||||
+ JETTY-1314 Handle bad URI encodings
|
||||
+ JETTY-1324 Tested not using CESU-8 instead of UTF-8
|
||||
|
|
|
@ -626,7 +626,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
|||
*/
|
||||
public abstract Session getSession(String idInCluster);
|
||||
|
||||
protected abstract void invalidateSessions();
|
||||
protected abstract void invalidateSessions() throws Exception;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -13,7 +13,19 @@
|
|||
|
||||
package org.eclipse.jetty.server.session;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
@ -28,7 +40,7 @@ import org.eclipse.jetty.util.MultiMap;
|
|||
*/
|
||||
public class HashSessionIdManager extends AbstractSessionIdManager
|
||||
{
|
||||
MultiMap<String> _sessions;
|
||||
private final Map<String, Set<WeakReference<HttpSession>>> _sessions = new HashMap<String, Set<WeakReference<HttpSession>>>();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public HashSessionIdManager()
|
||||
|
@ -77,7 +89,6 @@ public class HashSessionIdManager extends AbstractSessionIdManager
|
|||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
_sessions=new MultiMap<String>(true);
|
||||
super.doStart();
|
||||
}
|
||||
|
||||
|
@ -85,9 +96,7 @@ public class HashSessionIdManager extends AbstractSessionIdManager
|
|||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
if (_sessions!=null)
|
||||
_sessions.clear(); // Maybe invalidate?
|
||||
_sessions=null;
|
||||
_sessions.clear();
|
||||
super.doStop();
|
||||
}
|
||||
|
||||
|
@ -97,7 +106,10 @@ public class HashSessionIdManager extends AbstractSessionIdManager
|
|||
*/
|
||||
public boolean idInUse(String id)
|
||||
{
|
||||
return _sessions.containsKey(id);
|
||||
synchronized (this)
|
||||
{
|
||||
return _sessions.containsKey(id);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -106,7 +118,19 @@ public class HashSessionIdManager extends AbstractSessionIdManager
|
|||
*/
|
||||
public void addSession(HttpSession session)
|
||||
{
|
||||
_sessions.add(getClusterId(session.getId()),session);
|
||||
String id = getClusterId(session.getId());
|
||||
WeakReference<HttpSession> ref = new WeakReference<HttpSession>(session);
|
||||
|
||||
synchronized (this)
|
||||
{
|
||||
Set<WeakReference<HttpSession>> sessions = _sessions.get(id);
|
||||
if (sessions==null)
|
||||
{
|
||||
sessions=new HashSet<WeakReference<HttpSession>>();
|
||||
_sessions.put(id,sessions);
|
||||
}
|
||||
sessions.add(ref);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -115,7 +139,32 @@ public class HashSessionIdManager extends AbstractSessionIdManager
|
|||
*/
|
||||
public void removeSession(HttpSession session)
|
||||
{
|
||||
_sessions.removeValue(getClusterId(session.getId()),session);
|
||||
String id = getClusterId(session.getId());
|
||||
|
||||
synchronized (this)
|
||||
{
|
||||
Collection<WeakReference<HttpSession>> sessions = _sessions.get(id);
|
||||
if (sessions!=null)
|
||||
{
|
||||
for (Iterator<WeakReference<HttpSession>> iter = sessions.iterator(); iter.hasNext();)
|
||||
{
|
||||
WeakReference<HttpSession> ref = iter.next();
|
||||
HttpSession s=ref.get();
|
||||
if (s==null)
|
||||
{
|
||||
iter.remove();
|
||||
continue;
|
||||
}
|
||||
if (s==session)
|
||||
{
|
||||
iter.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sessions.isEmpty())
|
||||
_sessions.remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -124,19 +173,22 @@ public class HashSessionIdManager extends AbstractSessionIdManager
|
|||
*/
|
||||
public void invalidateAll(String id)
|
||||
{
|
||||
// Do not use iterators as this method tends to be called recursively
|
||||
// by the invalidate calls.
|
||||
while (_sessions.containsKey(id))
|
||||
{
|
||||
Session session=(Session)_sessions.getValue(id,0);
|
||||
|
||||
if (session.isValid())
|
||||
{
|
||||
session.invalidate();
|
||||
}
|
||||
|
||||
_sessions.removeValue(id,session);
|
||||
}
|
||||
Collection<WeakReference<HttpSession>> sessions;
|
||||
synchronized (this)
|
||||
{
|
||||
sessions = _sessions.remove(id);
|
||||
}
|
||||
|
||||
if (sessions!=null)
|
||||
{
|
||||
for (WeakReference<HttpSession> ref: sessions)
|
||||
{
|
||||
Session session=(Session)ref.get();
|
||||
if (session!=null && session.isValid())
|
||||
session.invalidate();
|
||||
}
|
||||
sessions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ import org.eclipse.jetty.util.log.Log;
|
|||
*/
|
||||
public class HashSessionManager extends AbstractSessionManager
|
||||
{
|
||||
protected final ConcurrentMap<String,HashedSession> _sessions=new ConcurrentHashMap<String,HashedSession>();
|
||||
private static int __id;
|
||||
private Timer _timer;
|
||||
private boolean _timerStop=false;
|
||||
|
@ -61,7 +62,6 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
private int _savePeriodMs=0; //don't do period saves by default
|
||||
private int _idleSavePeriodMs = 0; // don't idle save sessions by default.
|
||||
private TimerTask _saveTask;
|
||||
protected ConcurrentMap<String,HashedSession> _sessions;
|
||||
private File _storeDir;
|
||||
private boolean _lazyLoad=false;
|
||||
private volatile boolean _sessionsLoaded=false;
|
||||
|
@ -79,7 +79,6 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
@Override
|
||||
public void doStart() throws Exception
|
||||
{
|
||||
_sessions=new ConcurrentHashMap<String,HashedSession>();
|
||||
super.doStart();
|
||||
|
||||
_timerStop=false;
|
||||
|
@ -113,15 +112,7 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
@Override
|
||||
public void doStop() throws Exception
|
||||
{
|
||||
if (_storeDir != null)
|
||||
saveSessions();
|
||||
|
||||
super.doStop();
|
||||
|
||||
_sessions.clear();
|
||||
_sessions=null;
|
||||
|
||||
// stop the scavenger
|
||||
// stop the scavengers
|
||||
synchronized(this)
|
||||
{
|
||||
if (_saveTask!=null)
|
||||
|
@ -134,6 +125,12 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
_timer.cancel();
|
||||
_timer=null;
|
||||
}
|
||||
|
||||
// This will callback invalidate sessions - where we decide if we will save
|
||||
super.doStop();
|
||||
|
||||
_sessions.clear();
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -228,7 +225,7 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
{
|
||||
try
|
||||
{
|
||||
saveSessions();
|
||||
saveSessions(true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -381,16 +378,32 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void invalidateSessions()
|
||||
protected void invalidateSessions() throws Exception
|
||||
{
|
||||
// Invalidate all sessions to cause unbind events
|
||||
ArrayList<HashedSession> sessions=new ArrayList<HashedSession>(_sessions.values());
|
||||
for (Iterator<HashedSession> i=sessions.iterator(); i.hasNext();)
|
||||
int loop=100;
|
||||
while (sessions.size()>0 && loop-->0)
|
||||
{
|
||||
HashedSession session=i.next();
|
||||
session.invalidate();
|
||||
// If we are called from doStop
|
||||
if (isStopping() && _storeDir != null && _storeDir.exists() && _storeDir.canWrite())
|
||||
{
|
||||
// Then we only save and remove the session - it is not invalidated.
|
||||
for (HashedSession session : sessions)
|
||||
{
|
||||
session.save(false);
|
||||
removeSession(session,false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (HashedSession session : sessions)
|
||||
session.invalidate();
|
||||
}
|
||||
|
||||
// check that no new sessions were created while we were iterating
|
||||
sessions=new ArrayList<HashedSession>(_sessions.values());
|
||||
}
|
||||
_sessions.clear();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -412,7 +425,6 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
{
|
||||
return _sessions.remove(clusterId)!=null;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void setStoreDirectory (File dir)
|
||||
|
@ -485,10 +497,8 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void saveSessions() throws Exception
|
||||
public void saveSessions(boolean reactivate) throws Exception
|
||||
{
|
||||
if (_storeDir==null || !_storeDir.exists())
|
||||
{
|
||||
|
@ -501,48 +511,8 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
return;
|
||||
}
|
||||
|
||||
Iterator<Map.Entry<String, HashedSession>> itor = _sessions.entrySet().iterator();
|
||||
while (itor.hasNext())
|
||||
{
|
||||
Map.Entry<String,HashedSession> entry = itor.next();
|
||||
String id = entry.getKey();
|
||||
HashedSession session = entry.getValue();
|
||||
synchronized(session)
|
||||
{
|
||||
// No point saving a session that has been idled or has had a previous save failure
|
||||
if (!session.isIdled() && !session.isSaveFailed())
|
||||
{
|
||||
File file = null;
|
||||
FileOutputStream fos = null;
|
||||
try
|
||||
{
|
||||
file = new File (_storeDir, id);
|
||||
if (file.exists())
|
||||
file.delete();
|
||||
file.createNewFile();
|
||||
fos = new FileOutputStream (file);
|
||||
session.willPassivate();
|
||||
session.save(fos);
|
||||
session.didActivate();
|
||||
fos.close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
session.saveFailed();
|
||||
|
||||
Log.warn("Problem persisting session "+id, e);
|
||||
|
||||
if (fos != null)
|
||||
{
|
||||
// Must not leave files open if the saving failed
|
||||
IO.close(fos);
|
||||
// No point keeping the file if we didn't save the whole session
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (HashedSession session : _sessions.values())
|
||||
session.save(true);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -640,37 +610,64 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void invalidate ()
|
||||
protected void doInvalidate()
|
||||
throws IllegalStateException
|
||||
{
|
||||
if (isRunning())
|
||||
super.doInvalidate();
|
||||
|
||||
// Remove from the disk
|
||||
if (_storeDir!=null && getId()!=null)
|
||||
{
|
||||
super.invalidate();
|
||||
remove();
|
||||
String id=getId();
|
||||
File f = new File(_storeDir, id);
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void remove()
|
||||
private synchronized void save(boolean reactivate)
|
||||
{
|
||||
String id=getId();
|
||||
if (id==null)
|
||||
return;
|
||||
|
||||
//all sessions are invalidated when jetty is stopped, make sure we don't
|
||||
//remove all the sessions in this case
|
||||
if (isStopping() || isStopped())
|
||||
return;
|
||||
|
||||
if (_storeDir==null || !_storeDir.exists())
|
||||
// Only idle the session if not already idled and no previous save/idle has failed
|
||||
if (!isIdled() && !_saveFailed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
File f = new File(_storeDir, id);
|
||||
f.delete();
|
||||
}
|
||||
if (Log.isDebugEnabled())
|
||||
Log.debug("Saving {} {}",super.getId(),reactivate);
|
||||
|
||||
File file = null;
|
||||
FileOutputStream fos = null;
|
||||
|
||||
try
|
||||
{
|
||||
file = new File(_storeDir, super.getId());
|
||||
|
||||
if (file.exists())
|
||||
file.delete();
|
||||
file.createNewFile();
|
||||
fos = new FileOutputStream(file);
|
||||
willPassivate();
|
||||
save(fos);
|
||||
if (reactivate)
|
||||
didActivate();
|
||||
else
|
||||
clearAttributes();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
saveFailed(); // We won't try again for this session
|
||||
|
||||
Log.warn("Problem saving session " + super.getId(), e);
|
||||
|
||||
if (fos != null)
|
||||
{
|
||||
// Must not leave the file open if the saving failed
|
||||
IO.close(fos);
|
||||
// No point keeping the file if we didn't save the whole session
|
||||
file.delete();
|
||||
_idled=false; // assume problem was before _values.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* ------------------------------------------------------------ */
|
||||
public synchronized void save(OutputStream os) throws IOException
|
||||
{
|
||||
|
@ -751,6 +748,7 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Idle the session to reduce session memory footprint.
|
||||
|
@ -760,45 +758,7 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
*/
|
||||
public synchronized void idle()
|
||||
{
|
||||
// Only idle the session if not already idled and no previous save/idle has failed
|
||||
if (!isIdled() && !_saveFailed)
|
||||
{
|
||||
if (Log.isDebugEnabled())
|
||||
Log.debug("Idling " + super.getId());
|
||||
|
||||
File file = null;
|
||||
FileOutputStream fos = null;
|
||||
|
||||
try
|
||||
{
|
||||
file = new File(_storeDir, super.getId());
|
||||
|
||||
if (file.exists())
|
||||
file.delete();
|
||||
file.createNewFile();
|
||||
fos = new FileOutputStream(file);
|
||||
willPassivate();
|
||||
save(fos);
|
||||
clearAttributes();
|
||||
|
||||
_idled = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
saveFailed(); // We won't try again for this session
|
||||
|
||||
Log.warn("Problem idling session " + super.getId(), e);
|
||||
|
||||
if (fos != null)
|
||||
{
|
||||
// Must not leave the file open if the saving failed
|
||||
IO.close(fos);
|
||||
// No point keeping the file if we didn't save the whole session
|
||||
file.delete();
|
||||
_idled=false; // assume problem was before _values.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
save(false);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
Loading…
Reference in New Issue