001: // ========================================================================
002: // Copyright 1996-2005 Mort Bay Consulting Pty. Ltd.
003: // ------------------------------------------------------------------------
004: // Licensed under the Apache License, Version 2.0 (the "License");
005: // you may not use this file except in compliance with the License.
006: // You may obtain a copy of the License at
007: // http://www.apache.org/licenses/LICENSE-2.0
008: // Unless required by applicable law or agreed to in writing, software
009: // distributed under the License is distributed on an "AS IS" BASIS,
010: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: // See the License for the specific language governing permissions and
012: // limitations under the License.
013: // ========================================================================
014:
015: package org.mortbay.jetty.servlet;
016:
017: import java.util.ArrayList;
018: import java.util.Collections;
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.Map;
022:
023: import javax.servlet.http.HttpServletRequest;
024:
025: import org.mortbay.log.Log;
026: import org.mortbay.util.LazyList;
027:
028: /* ------------------------------------------------------------ */
029: /** An in-memory implementation of SessionManager.
030: *
031: * @author Greg Wilkins (gregw)
032: */
033: public class HashSessionManager extends AbstractSessionManager {
034: private int _scavengePeriodMs = 30000;
035: private Thread _scavenger = null;
036: protected Map _sessions;
037:
038: /* ------------------------------------------------------------ */
039: public HashSessionManager() {
040: super ();
041: }
042:
043: /* ------------------------------------------------------------ */
044: /* (non-Javadoc)
045: * @see org.mortbay.jetty.servlet.AbstractSessionManager#doStart()
046: */
047: public void doStart() throws Exception {
048: _sessions = new HashMap();
049: super .doStart();
050:
051: // Start the session scavenger if we haven't already
052: getSessionHandler().getServer().getThreadPool().dispatch(
053: new SessionScavenger());
054:
055: }
056:
057: /* ------------------------------------------------------------ */
058: /* (non-Javadoc)
059: * @see org.mortbay.jetty.servlet.AbstractSessionManager#doStop()
060: */
061: public void doStop() throws Exception {
062: super .doStop();
063: _sessions.clear();
064: _sessions = null;
065:
066: // stop the scavenger
067: Thread scavenger = _scavenger;
068: _scavenger = null;
069: if (scavenger != null)
070: scavenger.interrupt();
071:
072: }
073:
074: /* ------------------------------------------------------------ */
075: /**
076: * @return seconds
077: */
078: public int getScavengePeriod() {
079: return _scavengePeriodMs / 1000;
080: }
081:
082: /* ------------------------------------------------------------ */
083: public Map getSessionMap() {
084: return Collections.unmodifiableMap(_sessions);
085: }
086:
087: /* ------------------------------------------------------------ */
088: public int getSessions() {
089: return _sessions.size();
090: }
091:
092: /* ------------------------------------------------------------ */
093: public void setMaxInactiveInterval(int seconds) {
094: super .setMaxInactiveInterval(seconds);
095: if (_dftMaxIdleSecs > 0
096: && _scavengePeriodMs > _dftMaxIdleSecs * 1000)
097: setScavengePeriod((_dftMaxIdleSecs + 9) / 10);
098: }
099:
100: /* ------------------------------------------------------------ */
101: /**
102: * @param seconds
103: */
104: public void setScavengePeriod(int seconds) {
105: if (seconds == 0)
106: seconds = 60;
107:
108: int old_period = _scavengePeriodMs;
109: int period = seconds * 1000;
110: if (period > 60000)
111: period = 60000;
112: if (period < 1000)
113: period = 1000;
114:
115: if (period != old_period) {
116: synchronized (this ) {
117: _scavengePeriodMs = period;
118: if (_scavenger != null)
119: _scavenger.interrupt();
120: }
121: }
122: }
123:
124: /* -------------------------------------------------------------- */
125: /**
126: * Find sessions that have timed out and invalidate them. This runs in the
127: * SessionScavenger thread.
128: */
129: private void scavenge() {
130: Thread thread = Thread.currentThread();
131: ClassLoader old_loader = thread.getContextClassLoader();
132: try {
133: if (_loader != null)
134: thread.setContextClassLoader(_loader);
135:
136: long now = System.currentTimeMillis();
137:
138: // Since Hashtable enumeration is not safe over deletes,
139: // we build a list of stale sessions, then go back and invalidate
140: // them
141: Object stale = null;
142:
143: synchronized (HashSessionManager.this ) {
144: // For each session
145: for (Iterator i = _sessions.values().iterator(); i
146: .hasNext();) {
147: Session session = (Session) i.next();
148: long idleTime = session._maxIdleMs;
149: if (idleTime > 0
150: && session._accessed + idleTime < now) {
151: // Found a stale session, add it to the list
152: stale = LazyList.add(stale, session);
153: }
154: }
155: }
156:
157: // Remove the stale sessions
158: for (int i = LazyList.size(stale); i-- > 0;) {
159: // check it has not been accessed in the meantime
160: Session session = (Session) LazyList.get(stale, i);
161: long idleTime = session._maxIdleMs;
162: if (idleTime > 0
163: && session._accessed + idleTime < System
164: .currentTimeMillis()) {
165: session.invalidate();
166: int nbsess = this ._sessions.size();
167: if (nbsess < this ._minSessions)
168: this ._minSessions = nbsess;
169: }
170: }
171: } finally {
172: thread.setContextClassLoader(old_loader);
173: }
174: }
175:
176: /* ------------------------------------------------------------ */
177: protected void addSession(AbstractSessionManager.Session session) {
178: _sessions.put(session.getClusterId(), session);
179: }
180:
181: /* ------------------------------------------------------------ */
182: protected AbstractSessionManager.Session getSession(
183: String idInCluster) {
184: return (Session) _sessions.get(idInCluster);
185: }
186:
187: /* ------------------------------------------------------------ */
188: protected void invalidateSessions() {
189: // Invalidate all sessions to cause unbind events
190: ArrayList sessions = new ArrayList(_sessions.values());
191: for (Iterator i = sessions.iterator(); i.hasNext();) {
192: Session session = (Session) i.next();
193: session.invalidate();
194: }
195: _sessions.clear();
196:
197: }
198:
199: /* ------------------------------------------------------------ */
200: protected AbstractSessionManager.Session newSession(
201: HttpServletRequest request) {
202: return new Session(request);
203: }
204:
205: /* ------------------------------------------------------------ */
206: protected void removeSession(String idInCluster) {
207: _sessions.remove(idInCluster);
208: }
209:
210: /* ------------------------------------------------------------ */
211: /* ------------------------------------------------------------ */
212: /* ------------------------------------------------------------ */
213: protected class Session extends AbstractSessionManager.Session {
214: /* ------------------------------------------------------------ */
215: private static final long serialVersionUID = -2134521374206116367L;
216:
217: /* ------------------------------------------------------------- */
218: protected Session(HttpServletRequest request) {
219: super (request);
220: }
221:
222: /* ------------------------------------------------------------- */
223: public void setMaxInactiveInterval(int secs) {
224: super .setMaxInactiveInterval(secs);
225: if (_maxIdleMs > 0 && (_maxIdleMs / 10) < _scavengePeriodMs)
226: HashSessionManager.this
227: .setScavengePeriod((secs + 9) / 10);
228: }
229:
230: /* ------------------------------------------------------------ */
231: protected Map newAttributeMap() {
232: return new HashMap(3);
233: }
234: }
235:
236: /* ------------------------------------------------------------ */
237: /* ------------------------------------------------------------ */
238: /* -------------------------------------------------------------- */
239: /** SessionScavenger is a background thread that kills off old sessions */
240: class SessionScavenger implements Runnable {
241: public void run() {
242: _scavenger = Thread.currentThread();
243: String name = Thread.currentThread().getName();
244: if (_context != null)
245: Thread.currentThread().setName(
246: name + " - Invalidator - "
247: + _context.getContextPath());
248: int period = -1;
249: try {
250: do {
251: try {
252: if (period != _scavengePeriodMs) {
253: if (Log.isDebugEnabled())
254: Log.debug("Session scavenger period = "
255: + _scavengePeriodMs / 1000
256: + "s");
257: period = _scavengePeriodMs;
258: }
259: Thread.sleep(period > 1000 ? period : 1000);
260: HashSessionManager.this .scavenge();
261: } catch (InterruptedException ex) {
262: continue;
263: } catch (Error e) {
264: Log.warn(Log.EXCEPTION, e);
265: } catch (Exception e) {
266: Log.warn(Log.EXCEPTION, e);
267: }
268: } while (isStarted());
269: } finally {
270: HashSessionManager.this ._scavenger = null;
271: String exit = "Session scavenger exited";
272: if (isStarted())
273: Log.warn(exit);
274: else
275: Log.debug(exit);
276: Thread.currentThread().setName(name);
277: }
278: }
279:
280: } // SessionScavenger
281:
282: }
|