0001: /**********************************************************************************
0002: * $URL: https://source.sakaiproject.org/svn/tool/tags/sakai_2-4-1/tool-impl/impl/src/java/org/sakaiproject/tool/impl/SessionComponent.java $
0003: * $Id: SessionComponent.java 22832 2007-03-17 23:22:16Z ggolden@umich.edu $
0004: ***********************************************************************************
0005: *
0006: * Copyright (c) 2005, 2006, 2007 The Sakai Foundation.
0007: *
0008: * Licensed under the Educational Community License, Version 1.0 (the "License");
0009: * you may not use this file except in compliance with the License.
0010: * You may obtain a copy of the License at
0011: *
0012: * http://www.opensource.org/licenses/ecl1.php
0013: *
0014: * Unless required by applicable law or agreed to in writing, software
0015: * distributed under the License is distributed on an "AS IS" BASIS,
0016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: * See the License for the specific language governing permissions and
0018: * limitations under the License.
0019: *
0020: **********************************************************************************/package org.sakaiproject.tool.impl;
0021:
0022: import java.util.Collection;
0023: import java.util.Enumeration;
0024: import java.util.HashMap;
0025: import java.util.HashSet;
0026: import java.util.Iterator;
0027: import java.util.Map;
0028: import java.util.Set;
0029:
0030: import javax.servlet.ServletContext;
0031: import javax.servlet.http.HttpSession;
0032: import javax.servlet.http.HttpSessionBindingEvent;
0033: import javax.servlet.http.HttpSessionBindingListener;
0034: import javax.servlet.http.HttpSessionContext;
0035:
0036: import org.apache.commons.logging.Log;
0037: import org.apache.commons.logging.LogFactory;
0038: import org.sakaiproject.component.cover.ComponentManager;
0039: import org.sakaiproject.id.api.IdManager;
0040: import org.sakaiproject.thread_local.api.ThreadLocalManager;
0041: import org.sakaiproject.tool.api.ContextSession;
0042: import org.sakaiproject.tool.api.Session;
0043: import org.sakaiproject.tool.api.SessionBindingEvent;
0044: import org.sakaiproject.tool.api.SessionBindingListener;
0045: import org.sakaiproject.tool.api.SessionManager;
0046: import org.sakaiproject.tool.api.ToolSession;
0047: import org.sakaiproject.util.IteratorEnumeration;
0048:
0049: import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
0050:
0051: /**
0052: * <p>
0053: * Standard implementation of the Sakai SessionManager.
0054: * </p>
0055: */
0056: public abstract class SessionComponent implements SessionManager {
0057: /** Our log (commons). */
0058: private static Log M_log = LogFactory
0059: .getLog(SessionComponent.class);
0060:
0061: /** The sessions - keyed by session id. */
0062: protected Map m_sessions = new ConcurrentReaderHashMap();
0063:
0064: /** The maintenance. */
0065: protected Maintenance m_maintenance = null;
0066:
0067: /** Key in the ThreadLocalManager for binding our current session. */
0068: protected final static String CURRENT_SESSION = "org.sakaiproject.api.kernel.session.current";
0069:
0070: /** Key in the ThreadLocalManager for binding our current tool session. */
0071: protected final static String CURRENT_TOOL_SESSION = "org.sakaiproject.api.kernel.session.current.tool";
0072:
0073: /** Key in the ThreadLocalManager for access to the current servlet context (from tool-util/servlet/RequestFilter). */
0074: protected final static String CURRENT_SERVLET_CONTEXT = "org.sakaiproject.util.RequestFilter.servlet_context";
0075:
0076: /**********************************************************************************************************************************************************************************************************************************************************
0077: * Dependencies
0078: *********************************************************************************************************************************************************************************************************************************************************/
0079:
0080: /**
0081: * @return the ThreadLocalManager collaborator.
0082: */
0083: protected abstract ThreadLocalManager threadLocalManager();
0084:
0085: /**
0086: * @return the IdManager collaborator.
0087: */
0088: protected abstract IdManager idManager();
0089:
0090: /**********************************************************************************************************************************************************************************************************************************************************
0091: * Configuration
0092: *********************************************************************************************************************************************************************************************************************************************************/
0093:
0094: /** Configuration: default inactive period for sessions (seconds). */
0095: protected int m_defaultInactiveInterval = 30 * 60;
0096:
0097: /**
0098: * Configuration - set the default inactive period for sessions.
0099: *
0100: * @param value
0101: * The default inactive period for sessions.
0102: */
0103: public void setInactiveInterval(String value) {
0104: try {
0105: m_defaultInactiveInterval = Integer.parseInt(value);
0106: } catch (Throwable t) {
0107: System.out.println(t);
0108: }
0109: }
0110:
0111: /** Configuration: how often to check for inactive sessions (seconds). */
0112: protected int m_checkEvery = 60;
0113:
0114: /**
0115: * Configuration: set how often to check for inactive sessions (seconds).
0116: *
0117: * @param value
0118: * The how often to check for inactive sessions (seconds) value.
0119: */
0120: public void setCheckEvery(String value) {
0121: try {
0122: m_checkEvery = Integer.parseInt(value);
0123: } catch (Throwable t) {
0124: System.out.println(t);
0125: }
0126: }
0127:
0128: /**********************************************************************************************************************************************************************************************************************************************************
0129: * Init and Destroy
0130: *********************************************************************************************************************************************************************************************************************************************************/
0131:
0132: /**
0133: * Final initialization, once all dependencies are set.
0134: */
0135: public void init() {
0136: // start the maintenance thread
0137: if (m_checkEvery > 0) {
0138: m_maintenance = new Maintenance();
0139: m_maintenance.start();
0140: }
0141:
0142: M_log.info("init(): interval: " + m_defaultInactiveInterval
0143: + " refresh: " + m_checkEvery);
0144: }
0145:
0146: /**
0147: * Final cleanup.
0148: */
0149: public void destroy() {
0150: if (m_maintenance != null) {
0151: m_maintenance.stop();
0152: m_maintenance = null;
0153: }
0154:
0155: M_log.info("destroy()");
0156: }
0157:
0158: /**********************************************************************************************************************************************************************************************************************************************************
0159: * Work interface methods: SessionManager
0160: *********************************************************************************************************************************************************************************************************************************************************/
0161:
0162: /**
0163: * @inheritDoc
0164: */
0165: public Session getSession(String sessionId) {
0166: MySession s = (MySession) m_sessions.get(sessionId);
0167:
0168: return s;
0169: }
0170:
0171: /**
0172: * @inheritDoc
0173: */
0174: public Session startSession() {
0175: // create a new session
0176: Session s = new MySession();
0177:
0178: // remember it by id
0179: Session old = (Session) m_sessions.put(s.getId(), s);
0180:
0181: // check for id conflict
0182: if (old != null) {
0183: M_log.warn("startSession: duplication id: " + s.getId());
0184: }
0185:
0186: return s;
0187: }
0188:
0189: /**
0190: * @inheritDoc
0191: */
0192: public Session startSession(String id) {
0193: // create a new session
0194: Session s = new MySession(id);
0195:
0196: // remember it by id
0197: Session old = (Session) m_sessions.put(s.getId(), s);
0198:
0199: // check for id conflict
0200: if (old != null) {
0201: M_log
0202: .warn("startSession(id): duplication id: "
0203: + s.getId());
0204: }
0205:
0206: return s;
0207: }
0208:
0209: /**
0210: * @inheritDoc
0211: */
0212: public Session getCurrentSession() {
0213: Session rv = (Session) threadLocalManager()
0214: .get(CURRENT_SESSION);
0215:
0216: // if we don't have one already current, make one and bind it as current, but don't save it in our by-id table - let it just go away after the thread
0217: if (rv == null) {
0218: rv = new MySession();
0219: setCurrentSession(rv);
0220: }
0221:
0222: return rv;
0223: }
0224:
0225: /**
0226: * @inheritDoc
0227: */
0228: public String getCurrentSessionUserId() {
0229: Session s = (Session) threadLocalManager().get(CURRENT_SESSION);
0230: if (s != null) {
0231: return s.getUserId();
0232: }
0233:
0234: return null;
0235: }
0236:
0237: /**
0238: * @inheritDoc
0239: */
0240: public ToolSession getCurrentToolSession() {
0241: return (ToolSession) threadLocalManager().get(
0242: CURRENT_TOOL_SESSION);
0243: }
0244:
0245: /**
0246: * @inheritDoc
0247: */
0248: public void setCurrentSession(Session s) {
0249: threadLocalManager().set(CURRENT_SESSION, s);
0250: }
0251:
0252: /**
0253: * @inheritDoc
0254: */
0255: public void setCurrentToolSession(ToolSession s) {
0256: threadLocalManager().set(CURRENT_TOOL_SESSION, s);
0257: }
0258:
0259: /**
0260: * @inheritDoc
0261: */
0262: public int getActiveUserCount(int secs) {
0263: Set activeusers = new HashSet(m_sessions.size());
0264:
0265: long now = System.currentTimeMillis();
0266:
0267: for (Iterator i = m_sessions.values().iterator(); i.hasNext();) {
0268: MySession s = (MySession) i.next();
0269:
0270: if ((now - s.getLastAccessedTime()) < (secs * 1000)) {
0271: activeusers.add(s.getUserId());
0272: }
0273: }
0274:
0275: // Ignore admin and postmaster
0276: activeusers.remove("admin");
0277: activeusers.remove("postmaster");
0278: activeusers.remove(null);
0279:
0280: return activeusers.size();
0281: }
0282:
0283: /*************************************************************************************************************************************************
0284: * Entity: Session Also is an HttpSession
0285: ************************************************************************************************************************************************/
0286:
0287: public class MySession implements Session, HttpSession {
0288: /** Hold attributes in a Map. TODO: ConcurrentHashMap may be better for multiple writers */
0289: protected Map m_attributes = new ConcurrentReaderHashMap();
0290:
0291: /** Hold toolSessions in a Map, by placement id. TODO: ConcurrentHashMap may be better for multiple writers */
0292: protected Map m_toolSessions = new ConcurrentReaderHashMap();
0293:
0294: /** Hold context toolSessions in a Map, by context (webapp) id. TODO: ConcurrentHashMap may be better for multiple writers */
0295: protected Map m_contextSessions = new ConcurrentReaderHashMap();
0296:
0297: /** The creation time of the session. */
0298: protected long m_created = 0;
0299:
0300: /** The session id. */
0301: protected String m_id = null;
0302:
0303: /** Time last accessed (via getSession()). */
0304: protected long m_accessed = 0;
0305:
0306: /** Seconds of inactive time before being automatically invalidated - 0 turns off this feature. */
0307: protected int m_inactiveInterval = m_defaultInactiveInterval;
0308:
0309: /** The user id for this session. */
0310: protected String m_userId = null;
0311:
0312: /** The user enterprise id for this session. */
0313: protected String m_userEid = null;
0314:
0315: /** True while the session is valid. */
0316: protected boolean m_valid = true;
0317:
0318: public MySession() {
0319: m_id = idManager().createUuid();
0320: m_created = System.currentTimeMillis();
0321: m_accessed = m_created;
0322: }
0323:
0324: public MySession(String id) {
0325: m_id = id;
0326: m_created = System.currentTimeMillis();
0327: m_accessed = m_created;
0328: }
0329:
0330: /**
0331: * @inheritDoc
0332: */
0333: public Object getAttribute(String name) {
0334: return m_attributes.get(name);
0335: }
0336:
0337: /**
0338: * @inheritDoc
0339: */
0340: public Enumeration getAttributeNames() {
0341: return new IteratorEnumeration(m_attributes.keySet()
0342: .iterator());
0343: }
0344:
0345: /**
0346: * @inheritDoc
0347: */
0348: public long getCreationTime() {
0349: return m_created;
0350: }
0351:
0352: /**
0353: * @inheritDoc
0354: */
0355: public String getId() {
0356: return m_id;
0357: }
0358:
0359: /**
0360: * @inheritDoc
0361: */
0362: public long getLastAccessedTime() {
0363: return m_accessed;
0364: }
0365:
0366: /**
0367: * @inheritDoc
0368: */
0369: public int getMaxInactiveInterval() {
0370: return m_inactiveInterval;
0371: }
0372:
0373: /**
0374: * @inheritDoc
0375: */
0376: public String getUserEid() {
0377: return m_userEid;
0378: }
0379:
0380: /**
0381: * @inheritDoc
0382: */
0383: public String getUserId() {
0384: return m_userId;
0385: }
0386:
0387: /**
0388: * @inheritDoc
0389: */
0390: public void invalidate() {
0391: m_valid = false;
0392:
0393: // move the attributes and tool sessions to local maps in a synchronized block so the unbinding happens only on one thread
0394: Map unbindMap = null;
0395: Map toolMap = null;
0396: Map contextMap = null;
0397: synchronized (this ) {
0398: unbindMap = new HashMap(m_attributes);
0399: m_attributes.clear();
0400:
0401: toolMap = new HashMap(m_toolSessions);
0402: m_toolSessions.clear();
0403:
0404: contextMap = new HashMap(m_contextSessions);
0405: m_contextSessions.clear();
0406:
0407: // let it not be found
0408: m_sessions.remove(getId());
0409: }
0410:
0411: // clear each tool session
0412: for (Iterator i = toolMap.entrySet().iterator(); i
0413: .hasNext();) {
0414: Map.Entry e = (Map.Entry) i.next();
0415: ToolSession t = (ToolSession) e.getValue();
0416: t.clearAttributes();
0417: }
0418:
0419: // clear each context session
0420: for (Iterator i = contextMap.entrySet().iterator(); i
0421: .hasNext();) {
0422: Map.Entry e = (Map.Entry) i.next();
0423: ToolSession t = (ToolSession) e.getValue();
0424: t.clearAttributes();
0425: }
0426:
0427: // send unbind events
0428: for (Iterator i = unbindMap.entrySet().iterator(); i
0429: .hasNext();) {
0430: Map.Entry e = (Map.Entry) i.next();
0431: String name = (String) e.getKey();
0432: Object value = e.getValue();
0433: unBind(name, value);
0434: }
0435:
0436: // if this is the current session, remove it
0437: if (this .equals(getCurrentSession())) {
0438: setCurrentSession(null);
0439: }
0440: }
0441:
0442: /**
0443: * {@inheritDoc}
0444: */
0445: public void clear() {
0446: // move the attributes and tool sessions to local maps in a synchronized block so the unbinding happens only on one thread
0447: Map unbindMap = null;
0448: Map toolMap = null;
0449: Map contextMap = null;
0450: synchronized (this ) {
0451: unbindMap = new HashMap(m_attributes);
0452: m_attributes.clear();
0453:
0454: toolMap = new HashMap(m_toolSessions);
0455: m_toolSessions.clear();
0456:
0457: contextMap = new HashMap(m_contextSessions);
0458: m_contextSessions.clear();
0459: }
0460:
0461: // clear each tool session
0462: for (Iterator i = toolMap.entrySet().iterator(); i
0463: .hasNext();) {
0464: Map.Entry e = (Map.Entry) i.next();
0465: ToolSession t = (ToolSession) e.getValue();
0466: t.clearAttributes();
0467: }
0468:
0469: // clear each context session
0470: for (Iterator i = contextMap.entrySet().iterator(); i
0471: .hasNext();) {
0472: Map.Entry e = (Map.Entry) i.next();
0473: ToolSession t = (ToolSession) e.getValue();
0474: t.clearAttributes();
0475: }
0476:
0477: // send unbind events
0478: for (Iterator i = unbindMap.entrySet().iterator(); i
0479: .hasNext();) {
0480: Map.Entry e = (Map.Entry) i.next();
0481: String name = (String) e.getKey();
0482: Object value = e.getValue();
0483: unBind(name, value);
0484: }
0485: }
0486:
0487: /**
0488: * {@inheritDoc}
0489: */
0490: public void clearExcept(Collection names) {
0491: // save any attributes in names
0492: Map saveAttributes = new HashMap();
0493: for (Iterator i = names.iterator(); i.hasNext();) {
0494: String name = (String) i.next();
0495: Object value = m_attributes.get(name);
0496: if (value != null) {
0497: // remvove, but do NOT unbind
0498: m_attributes.remove(name);
0499: saveAttributes.put(name, value);
0500: }
0501: }
0502:
0503: // clear the remaining
0504: clear();
0505:
0506: // restore the saved attributes
0507: for (Iterator i = saveAttributes.entrySet().iterator(); i
0508: .hasNext();) {
0509: Map.Entry e = (Map.Entry) i.next();
0510: String name = (String) e.getKey();
0511: Object value = e.getValue();
0512: m_attributes.put(name, value);
0513: }
0514: }
0515:
0516: /**
0517: * @inheritDoc
0518: */
0519: public void setActive() {
0520: m_accessed = System.currentTimeMillis();
0521: }
0522:
0523: /**
0524: * @inheritDoc
0525: */
0526: public void removeAttribute(String name) {
0527: // remove
0528: Object value = m_attributes.remove(name);
0529:
0530: // unbind event
0531: unBind(name, value);
0532: }
0533:
0534: /**
0535: * @inheritDoc
0536: */
0537: public void setAttribute(String name, Object value) {
0538: // treat a set to null as a remove
0539: if (value == null) {
0540: removeAttribute(name);
0541: }
0542:
0543: else {
0544: // add
0545: Object old = m_attributes.put(name, value);
0546:
0547: // bind event
0548: bind(name, value);
0549:
0550: // unbind event if old exiss
0551: if (old != null) {
0552: unBind(name, old);
0553: }
0554: }
0555: }
0556:
0557: /**
0558: * @inheritDoc
0559: */
0560: public void setMaxInactiveInterval(int interval) {
0561: m_inactiveInterval = interval;
0562: }
0563:
0564: /**
0565: * @inheritDoc
0566: */
0567: public void setUserEid(String eid) {
0568: m_userEid = eid;
0569: }
0570:
0571: /**
0572: * @inheritDoc
0573: */
0574: public void setUserId(String uid) {
0575: m_userId = uid;
0576: }
0577:
0578: /**
0579: * @inheritDoc
0580: */
0581: public ToolSession getToolSession(String placementId) {
0582: ToolSession t = (ToolSession) m_toolSessions
0583: .get(placementId);
0584: if (t == null) {
0585: t = new MyLittleSession(this , placementId);
0586: m_toolSessions.put(placementId, t);
0587: }
0588:
0589: // mark it as accessed
0590: ((MyLittleSession) t).setAccessed();
0591:
0592: return t;
0593: }
0594:
0595: /**
0596: * @inheritDoc
0597: */
0598: public ContextSession getContextSession(String contextId) {
0599: ContextSession t = (ContextSession) m_contextSessions
0600: .get(contextId);
0601: if (t == null) {
0602: t = new MyLittleSession(this , contextId);
0603: m_contextSessions.put(contextId, t);
0604: }
0605:
0606: // mark it as accessed
0607: ((MyLittleSession) t).setAccessed();
0608:
0609: return t;
0610: }
0611:
0612: /**
0613: * Check if the session has become inactive
0614: *
0615: * @return true if the session is capable of becoming inactive and has done so, false if not.
0616: */
0617: protected boolean isInactive() {
0618: return ((m_inactiveInterval > 0) && (System
0619: .currentTimeMillis() > (m_accessed + (m_inactiveInterval * 1000))));
0620: }
0621:
0622: /**
0623: * {@inheritDoc}
0624: */
0625: public boolean equals(Object obj) {
0626: if (!(obj instanceof Session)) {
0627: return false;
0628: }
0629:
0630: return ((Session) obj).getId().equals(getId());
0631: }
0632:
0633: /**
0634: * {@inheritDoc}
0635: */
0636: public int hashCode() {
0637: return getId().hashCode();
0638: }
0639:
0640: /**
0641: * {@inheritDoc}
0642: */
0643: public ServletContext getServletContext() {
0644: return (ServletContext) threadLocalManager().get(
0645: CURRENT_SERVLET_CONTEXT);
0646: }
0647:
0648: /**
0649: * {@inheritDoc}
0650: */
0651: public HttpSessionContext getSessionContext() {
0652: throw new UnsupportedOperationException();
0653: }
0654:
0655: /**
0656: * {@inheritDoc}
0657: */
0658: public Object getValue(String arg0) {
0659: throw new UnsupportedOperationException();
0660: }
0661:
0662: /**
0663: * {@inheritDoc}
0664: */
0665: public String[] getValueNames() {
0666: throw new UnsupportedOperationException();
0667: }
0668:
0669: /**
0670: * {@inheritDoc}
0671: */
0672: public void putValue(String arg0, Object arg1) {
0673: throw new UnsupportedOperationException();
0674: }
0675:
0676: /**
0677: * {@inheritDoc}
0678: */
0679: public void removeValue(String arg0) {
0680: throw new UnsupportedOperationException();
0681: }
0682:
0683: /**
0684: * {@inheritDoc}
0685: */
0686: public boolean isNew() {
0687: return false;
0688: }
0689:
0690: /**
0691: * Unbind the value if it's a SessionBindingListener. Also does the HTTP unbinding if it's a HttpSessionBindingListener.
0692: *
0693: * @param name
0694: * The attribute name bound.
0695: * @param value
0696: * The bond value.
0697: */
0698: protected void unBind(String name, Object value) {
0699: if (value instanceof SessionBindingListener) {
0700: SessionBindingEvent event = new MySessionBindingEvent(
0701: name, this , value);
0702: ((SessionBindingListener) value).valueUnbound(event);
0703: }
0704:
0705: // also unbind any objects that are regular HttpSessionBindingListeners
0706: if (value instanceof HttpSessionBindingListener) {
0707: HttpSessionBindingEvent event = new HttpSessionBindingEvent(
0708: this , name, value);
0709: ((HttpSessionBindingListener) value)
0710: .valueUnbound(event);
0711: }
0712: }
0713:
0714: /**
0715: * Bind the value if it's a SessionBindingListener. Also does the HTTP binding if it's a HttpSessionBindingListener.
0716: *
0717: * @param name
0718: * The attribute name bound.
0719: * @param value
0720: * The bond value.
0721: */
0722: protected void bind(String name, Object value) {
0723: if (value instanceof SessionBindingListener) {
0724: SessionBindingEvent event = new MySessionBindingEvent(
0725: name, this , value);
0726: ((SessionBindingListener) value).valueBound(event);
0727: }
0728:
0729: // also bind any objects that are regular HttpSessionBindingListeners
0730: if (value instanceof HttpSessionBindingListener) {
0731: HttpSessionBindingEvent event = new HttpSessionBindingEvent(
0732: this , name, value);
0733: ((HttpSessionBindingListener) value).valueBound(event);
0734: }
0735: }
0736: }
0737:
0738: /**********************************************************************************************************************************************************************************************************************************************************
0739: * Entity: SessionBindingEvent
0740: *********************************************************************************************************************************************************************************************************************************************************/
0741:
0742: public class MySessionBindingEvent implements SessionBindingEvent {
0743: /** The attribute name. */
0744: protected String m_name = null;
0745:
0746: /** The session. */
0747: protected Session m_session = null;
0748:
0749: /** The value. */
0750: protected Object m_value = null;
0751:
0752: /**
0753: * Construct.
0754: *
0755: * @param name
0756: * The name.
0757: * @param session
0758: * The session.
0759: * @param value
0760: * The value.
0761: */
0762: MySessionBindingEvent(String name, Session session, Object value) {
0763: m_name = name;
0764: m_session = session;
0765: m_value = value;
0766: }
0767:
0768: /**
0769: * @inheritDoc
0770: */
0771: public String getName() {
0772: return m_name;
0773: }
0774:
0775: /**
0776: * @inheritDoc
0777: */
0778: public Session getSession() {
0779: return m_session;
0780: }
0781:
0782: /**
0783: * @inheritDoc
0784: */
0785: public Object getValue() {
0786: return m_value;
0787: }
0788: }
0789:
0790: /**********************************************************************************************************************************************************************************************************************************************************
0791: * Entity: ToolSession, ContextSession (and even HttpSession)
0792: *********************************************************************************************************************************************************************************************************************************************************/
0793:
0794: public class MyLittleSession implements ToolSession,
0795: ContextSession, HttpSession {
0796: /** Hold attributes in a Map. TODO: ConcurrentHashMap may be better for multiple writers */
0797: protected Map m_attributes = new ConcurrentReaderHashMap();
0798:
0799: /** The creation time of the session. */
0800: protected long m_created = 0;
0801:
0802: /** The session id. */
0803: protected String m_id = null;
0804:
0805: /** The tool placement / context id. */
0806: protected String m_littleId = null;
0807:
0808: /** The sakai session in which I live. */
0809: protected Session m_session = null;
0810:
0811: /** Time last accessed (via getSession()). */
0812: protected long m_accessed = 0;
0813:
0814: public MyLittleSession(Session s, String id) {
0815: m_id = idManager().createUuid();
0816: m_created = System.currentTimeMillis();
0817: m_accessed = m_created;
0818: m_littleId = id;
0819: m_session = s;
0820: }
0821:
0822: /**
0823: * @inheritDoc
0824: */
0825: public Object getAttribute(String name) {
0826: return m_attributes.get(name);
0827: }
0828:
0829: /**
0830: * @inheritDoc
0831: */
0832: public Enumeration getAttributeNames() {
0833: return new IteratorEnumeration(m_attributes.keySet()
0834: .iterator());
0835: }
0836:
0837: /**
0838: * @inheritDoc
0839: */
0840: public long getCreationTime() {
0841: return m_created;
0842: }
0843:
0844: /**
0845: * @inheritDoc
0846: */
0847: public String getId() {
0848: return m_id;
0849: }
0850:
0851: /**
0852: * @inheritDoc
0853: */
0854: public long getLastAccessedTime() {
0855: return m_accessed;
0856: }
0857:
0858: /**
0859: * @inheritDoc
0860: */
0861: public String getPlacementId() {
0862: return m_littleId;
0863: }
0864:
0865: /**
0866: * @inheritDoc
0867: */
0868: public String getContextId() {
0869: return m_littleId;
0870: }
0871:
0872: /**
0873: * @inheritDoc
0874: */
0875: public void clearAttributes() {
0876: // move the attributes to a local map in a synchronized block so the unbinding happens only on one thread
0877: Map unbindMap = null;
0878: synchronized (this ) {
0879: unbindMap = new HashMap(m_attributes);
0880: m_attributes.clear();
0881: }
0882:
0883: // send unbind events
0884: for (Iterator i = unbindMap.entrySet().iterator(); i
0885: .hasNext();) {
0886: Map.Entry e = (Map.Entry) i.next();
0887: String name = (String) e.getKey();
0888: Object value = e.getValue();
0889: unBind(name, value);
0890: }
0891: }
0892:
0893: /**
0894: * Mark the session as just accessed.
0895: */
0896: protected void setAccessed() {
0897: m_accessed = System.currentTimeMillis();
0898: }
0899:
0900: /**
0901: * @inheritDoc
0902: */
0903: public void removeAttribute(String name) {
0904: // remove
0905: Object value = m_attributes.remove(name);
0906:
0907: // unbind event
0908: unBind(name, value);
0909: }
0910:
0911: /**
0912: * @inheritDoc
0913: */
0914: public void setAttribute(String name, Object value) {
0915: // treat a set to null as a remove
0916: if (value == null) {
0917: removeAttribute(name);
0918: }
0919:
0920: else {
0921: // add
0922: Object old = m_attributes.put(name, value);
0923:
0924: // bind event
0925: bind(name, value);
0926:
0927: // unbind event if old exiss
0928: if (old != null) {
0929: unBind(name, old);
0930: }
0931: }
0932: }
0933:
0934: /**
0935: * {@inheritDoc}
0936: */
0937: public boolean equals(Object obj) {
0938: if (!(obj instanceof ToolSession)) {
0939: return false;
0940: }
0941:
0942: return ((ToolSession) obj).getId().equals(getId());
0943: }
0944:
0945: /**
0946: * {@inheritDoc}
0947: */
0948: public int hashCode() {
0949: return getId().hashCode();
0950: }
0951:
0952: /**
0953: * Unbind the value if it's a SessionBindingListener. Also does the HTTP unbinding if it's a HttpSessionBindingListener.
0954: *
0955: * @param name
0956: * The attribute name bound.
0957: * @param value
0958: * The bond value.
0959: */
0960: protected void unBind(String name, Object value) {
0961: if (value instanceof SessionBindingListener) {
0962: SessionBindingEvent event = new MySessionBindingEvent(
0963: name, null, value);
0964: ((SessionBindingListener) value).valueUnbound(event);
0965: }
0966:
0967: // also unbind any objects that are regular HttpSessionBindingListeners
0968: if (value instanceof HttpSessionBindingListener) {
0969: HttpSessionBindingEvent event = new HttpSessionBindingEvent(
0970: this , name, value);
0971: ((HttpSessionBindingListener) value)
0972: .valueUnbound(event);
0973: }
0974: }
0975:
0976: /**
0977: * Bind the value if it's a SessionBindingListener. Also does the HTTP binding if it's a HttpSessionBindingListener.
0978: *
0979: * @param name
0980: * The attribute name bound.
0981: * @param value
0982: * The bond value.
0983: */
0984: protected void bind(String name, Object value) {
0985: if (value instanceof SessionBindingListener) {
0986: SessionBindingEvent event = new MySessionBindingEvent(
0987: name, m_session, value);
0988: ((SessionBindingListener) value).valueBound(event);
0989: }
0990:
0991: if (value instanceof HttpSessionBindingListener) {
0992: HttpSessionBindingEvent event = new HttpSessionBindingEvent(
0993: this , name, value);
0994: ((HttpSessionBindingListener) value).valueBound(event);
0995: }
0996: }
0997:
0998: /**
0999: * @inheritDoc
1000: */
1001: public String getUserEid() {
1002: return m_session.getUserEid();
1003: }
1004:
1005: /**
1006: * @inheritDoc
1007: */
1008: public String getUserId() {
1009: return m_session.getUserId();
1010: }
1011:
1012: /**
1013: * @inheritDoc
1014: */
1015: public ServletContext getServletContext() {
1016: return (ServletContext) threadLocalManager().get(
1017: CURRENT_SERVLET_CONTEXT);
1018: }
1019:
1020: /**
1021: * @inheritDoc
1022: */
1023: public void setMaxInactiveInterval(int arg0) {
1024: // TODO: just ignore this ?
1025: }
1026:
1027: /**
1028: * @inheritDoc
1029: */
1030: public int getMaxInactiveInterval() {
1031: return m_session.getMaxInactiveInterval();
1032: }
1033:
1034: /**
1035: * @inheritDoc
1036: */
1037: public HttpSessionContext getSessionContext() {
1038: throw new UnsupportedOperationException();
1039: }
1040:
1041: /**
1042: * @inheritDoc
1043: */
1044: public Object getValue(String arg0) {
1045: throw new UnsupportedOperationException();
1046: }
1047:
1048: /**
1049: * @inheritDoc
1050: */
1051: public String[] getValueNames() {
1052: throw new UnsupportedOperationException();
1053: }
1054:
1055: /**
1056: * @inheritDoc
1057: */
1058: public void putValue(String arg0, Object arg1) {
1059: throw new UnsupportedOperationException();
1060: }
1061:
1062: /**
1063: * @inheritDoc
1064: */
1065: public void removeValue(String arg0) {
1066: throw new UnsupportedOperationException();
1067: }
1068:
1069: /**
1070: * @inheritDoc
1071: */
1072: public void invalidate() {
1073: clearAttributes();
1074: // TODO: cause to go away?
1075: }
1076:
1077: /**
1078: * @inheritDoc
1079: */
1080: public boolean isNew() {
1081: return false;
1082: }
1083: }
1084:
1085: /**********************************************************************************************************************************************************************************************************************************************************
1086: * Maintenance
1087: *********************************************************************************************************************************************************************************************************************************************************/
1088:
1089: protected class Maintenance implements Runnable {
1090: /** My thread running my timeout checker. */
1091: protected Thread m_maintenanceChecker = null;
1092:
1093: /** Signal to the timeout checker to stop. */
1094: protected boolean m_maintenanceCheckerStop = false;
1095:
1096: /**
1097: * Construct.
1098: */
1099: public Maintenance() {
1100: }
1101:
1102: /**
1103: * Start the maintenance thread.
1104: */
1105: public void start() {
1106: if (m_maintenanceChecker != null)
1107: return;
1108:
1109: m_maintenanceChecker = new Thread(this ,
1110: "Sakai.SessionComponent.Maintenance");
1111: m_maintenanceCheckerStop = false;
1112: m_maintenanceChecker.start();
1113: }
1114:
1115: /**
1116: * Stop the maintenance thread.
1117: */
1118: public void stop() {
1119: if (m_maintenanceChecker != null) {
1120: m_maintenanceCheckerStop = true;
1121: m_maintenanceChecker.interrupt();
1122: try {
1123: // wait for it to die
1124: m_maintenanceChecker.join();
1125: } catch (InterruptedException ignore) {
1126: }
1127: m_maintenanceChecker = null;
1128: }
1129: }
1130:
1131: /**
1132: * Run the maintenance thread. Every m_checkEvery seconds, check for expired sessions.
1133: */
1134: public void run() {
1135: // since we might be running while the component manager is still being created and populated, such as at server
1136: // startup, wait here for a complete component manager
1137: ComponentManager.waitTillConfigured();
1138:
1139: while (!m_maintenanceCheckerStop) {
1140: try {
1141: for (Iterator i = m_sessions.values().iterator(); i
1142: .hasNext();) {
1143: MySession s = (MySession) i.next();
1144: if (M_log.isDebugEnabled())
1145: M_log
1146: .debug("checking session "
1147: + s.getId());
1148: if (s.isInactive()) {
1149: if (M_log.isDebugEnabled())
1150: M_log.debug("invalidating session "
1151: + s.getId());
1152: s.invalidate();
1153: }
1154: }
1155: } catch (Throwable e) {
1156: M_log.warn("run(): exception: " + e);
1157: } finally {
1158: }
1159:
1160: // cycle every REFRESH seconds
1161: if (!m_maintenanceCheckerStop) {
1162: try {
1163: Thread.sleep(m_checkEvery * 1000L);
1164: } catch (Exception ignore) {
1165: }
1166: }
1167: }
1168: }
1169: }
1170: }
|