/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/kernel/trunk/kernel-impl/src/main/java/org/sakaiproject/tool/impl/MyLittleSession.java $ * $Id: MyLittleSession.java 105077 2012-02-24 22:54:29Z ottenhoff@longsight.com $ ********************************************************************************** * * Copyright (c) 2008 The Sakai Foundation. * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **********************************************************************************/ package org.sakaiproject.tool.impl; import java.io.Serializable; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.ServletContext; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import javax.servlet.http.HttpSessionContext; import org.apache.commons.collections.iterators.IteratorChain; import org.sakaiproject.thread_local.api.ThreadLocalManager; import org.sakaiproject.tool.api.ContextSession; import org.sakaiproject.tool.api.NonPortableSession; import org.sakaiproject.tool.api.Session; import org.sakaiproject.tool.api.SessionAttributeListener; import org.sakaiproject.tool.api.SessionBindingEvent; import org.sakaiproject.tool.api.SessionBindingListener; import org.sakaiproject.tool.api.SessionManager; import org.sakaiproject.tool.api.SessionStore; import org.sakaiproject.tool.api.ToolSession; import org.sakaiproject.util.IteratorEnumeration; /********************************************************************************************************************************************************************************************************************************************************** * Entity: ToolSession, ContextSession (and even HttpSession) *********************************************************************************************************************************************************************************************************************************************************/ public class MyLittleSession implements ToolSession, ContextSession, HttpSession, Serializable { /** * Value that identifies the version of this class that has been Serialized. */ private static final long serialVersionUID = 1L; /** * SessionManager */ private transient SessionManager sessionManager; private transient SessionStore sessionStore; private transient ThreadLocalManager threadLocalManager; private transient boolean TERRACOTTA_CLUSTER; private transient NonPortableSession m_nonPortalSession; private transient SessionAttributeListener sessionListener; /** Hold attributes in a Map. TODO: ConcurrentHashMap may be better for multiple writers */ protected Map m_attributes = new ConcurrentHashMap(); /** The creation time of the session. */ protected long m_created = 0; /** The session id. */ protected String m_id = null; /** The tool placement / context id. */ protected String m_littleId = null; /** The sakai session in which I live. */ protected Session m_session = null; /** Time last accessed (via getSession()). */ protected long m_accessed = 0; public MyLittleSession(SessionManager sessionManager, String id, Session s, String littleId, ThreadLocalManager threadLocalManager, SessionAttributeListener sessionListener, SessionStore sessionStore, NonPortableSession nonPortableSession) { this.sessionManager = sessionManager; this.m_id = id; this.m_session = s; this.m_littleId = littleId; this.threadLocalManager = threadLocalManager; this.sessionStore = sessionStore; this.m_nonPortalSession = nonPortableSession; this.sessionListener = sessionListener; m_created = System.currentTimeMillis(); m_accessed = m_created; String clusterTerracotta = System.getProperty("sakai.cluster.terracotta"); TERRACOTTA_CLUSTER = "true".equals(clusterTerracotta); } protected void resolveTransientFields() { // These are spelled out instead of using imports, to be explicit org.sakaiproject.component.api.ComponentManager compMgr = org.sakaiproject.component.cover.ComponentManager.getInstance(); sessionManager = (SessionManager)compMgr.get(org.sakaiproject.tool.api.SessionManager.class); sessionStore = (SessionStore)compMgr.get(org.sakaiproject.tool.api.SessionStore.class); threadLocalManager = (ThreadLocalManager)compMgr.get(org.sakaiproject.thread_local.api.ThreadLocalManager.class); // set the TERRACOTTA_CLUSTER flag resolveTerracottaClusterProperty(); m_nonPortalSession = new MyNonPortableSession(); sessionListener = (SessionAttributeListener)compMgr.get(org.sakaiproject.tool.api.SessionBindingListener.class); } protected void resolveTerracottaClusterProperty() { String clusterTerracotta = System.getProperty("sakai.cluster.terracotta"); TERRACOTTA_CLUSTER = "true".equals(clusterTerracotta); } /** * @inheritDoc */ public Object getAttribute(String name) { Object target = m_attributes.get(name); if ((target == null) && (m_nonPortalSession != null)) { target = m_nonPortalSession.getAttribute(name); } return target; } /** * @inheritDoc */ public Enumeration getAttributeNames() { IteratorChain ic = new IteratorChain(m_attributes.keySet().iterator(),m_nonPortalSession.getAllAttributes().keySet().iterator()); return new IteratorEnumeration(ic); } /** * @inheritDoc */ public long getCreationTime() { return m_created; } /** * @inheritDoc */ public String getId() { return m_id; } /** * @inheritDoc */ public long getLastAccessedTime() { return m_accessed; } /** * @inheritDoc */ public String getPlacementId() { return m_littleId; } /** * @inheritDoc */ public String getContextId() { return m_littleId; } /** * @inheritDoc */ public void clearAttributes() { // move the attributes to a local map in a synchronized block so the unbinding happens only on one thread Map unbindMap = null; Map<String,Object> nonPortableMap = null; synchronized (this) { unbindMap = new HashMap(m_attributes); m_attributes.clear(); nonPortableMap = m_nonPortalSession.getAllAttributes(); m_nonPortalSession.clear(); } // send unbind events for (Iterator i = unbindMap.entrySet().iterator(); i.hasNext();) { Map.Entry e = (Map.Entry) i.next(); String name = (String) e.getKey(); Object value = e.getValue(); unBind(name, value); } // send unbind events for non clustered session data (in a clustered environment) for (Map.Entry<String, Object> e: nonPortableMap.entrySet()) { unBind(e.getKey(), e.getValue()); } } /** * Mark the session as just accessed. */ protected void setAccessed() { m_accessed = System.currentTimeMillis(); } /** * @inheritDoc */ public void removeAttribute(String name) { // remove Object value = m_attributes.remove(name); if ((value == null) && (m_nonPortalSession != null)) { value = m_nonPortalSession.removeAttribute(name); } // unbind event unBind(name, value); } /** * @inheritDoc */ public void setAttribute(String name, Object value) { // treat a set to null as a remove if (value == null) { removeAttribute(name); } else { Object old = null; // If this is not a terracotta clustered environment then immediately // place the attribute in the normal data structure // Otherwise, if this *IS* a TERRACOTTA_CLUSTER, then check the current // tool id against the tool whitelist, to see if attributes from this // tool should be clustered, or not. if ((!TERRACOTTA_CLUSTER) || (sessionStore.isCurrentToolClusterable())) { old = m_attributes.put(name, value); } else { old = m_nonPortalSession.setAttribute(name, value); } // bind event bind(name, value); // unbind event if old exiss if (old != null) { unBind(name, old); } } } /** * {@inheritDoc} */ public boolean equals(Object obj) { if (!(obj instanceof ToolSession)) { return false; } return ((ToolSession) obj).getId().equals(getId()); } /** * {@inheritDoc} */ public int hashCode() { return getId().hashCode(); } /** * Unbind the value if it's a SessionBindingListener. Also does the HTTP unbinding if it's a HttpSessionBindingListener. * * @param name * The attribute name bound. * @param value * The bond value. */ protected void unBind(String name, Object value) { if (value instanceof SessionBindingListener) { SessionBindingEvent event = new MySessionBindingEvent(name, null, value); ((SessionBindingListener) value).valueUnbound(event); } // also unbind any objects that are regular HttpSessionBindingListeners if (value instanceof HttpSessionBindingListener) { HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, name, value); ((HttpSessionBindingListener) value).valueUnbound(event); } // Added for testing purposes. Very much unsure whether this is a proper // use of MySessionBindingEvent. if ( sessionListener != null ) { sessionListener.attributeRemoved(new MySessionBindingEvent(name, m_session, value)); } } /** * Bind the value if it's a SessionBindingListener. Also does the HTTP binding if it's a HttpSessionBindingListener. * * @param name * The attribute name bound. * @param value * The bond value. */ protected void bind(String name, Object value) { if (value instanceof SessionBindingListener) { SessionBindingEvent event = new MySessionBindingEvent(name, m_session, value); ((SessionBindingListener) value).valueBound(event); } if (value instanceof HttpSessionBindingListener) { HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, name, value); ((HttpSessionBindingListener) value).valueBound(event); } // Added for testing purposes. Very much unsure whether this is a proper // use of MySessionBindingEvent. if ( sessionListener != null ) { sessionListener.attributeAdded(new MySessionBindingEvent(name, m_session, value)); } } /** * @inheritDoc */ public String getUserEid() { return m_session.getUserEid(); } /** * @inheritDoc */ public String getUserId() { return m_session.getUserId(); } /** * @inheritDoc */ public ServletContext getServletContext() { return (ServletContext) threadLocalManager.get(SessionComponent.CURRENT_SERVLET_CONTEXT); } /** * @inheritDoc */ public void setMaxInactiveInterval(int arg0) { // TODO: just ignore this ? } /** * @inheritDoc */ public int getMaxInactiveInterval() { return m_session.getMaxInactiveInterval(); } /** * @inheritDoc */ public HttpSessionContext getSessionContext() { throw new UnsupportedOperationException(); } /** * @inheritDoc */ public Object getValue(String arg0) { throw new UnsupportedOperationException(); } /** * @inheritDoc */ public String[] getValueNames() { throw new UnsupportedOperationException(); } /** * @inheritDoc */ public void putValue(String arg0, Object arg1) { throw new UnsupportedOperationException(); } /** * @inheritDoc */ public void removeValue(String arg0) { throw new UnsupportedOperationException(); } /** * @inheritDoc */ public void invalidate() { clearAttributes(); // TODO: cause to go away? } /** * @inheritDoc */ public boolean isNew() { return false; } }