org.sakaiproject.tool.impl.MyLittleSession.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.tool.impl.MyLittleSession.java

Source

/**********************************************************************************
 * $URL$
 * $Id$
 **********************************************************************************
 *
 * 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 org.apache.commons.collections.iterators.IteratorChain;
import org.sakaiproject.thread_local.api.ThreadLocalManager;
import org.sakaiproject.tool.api.*;
import org.sakaiproject.util.IteratorEnumeration;

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 java.io.Serializable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**********************************************************************************************************************************************************************************************************************************************************
 * Entity: ToolSession, ContextSession (and even HttpSession)
 *********************************************************************************************************************************************************************************************************************************************************/

public class MyLittleSession implements ToolSession, ContextSession, HttpSession, Serializable {
    public static final String TYPE_TOOL = "tool";
    /**
     * Identify this as a tool or context session concretely
     */
    protected String m_type = TYPE_TOOL;
    public static final String TYPE_CONTEXT = "context";
    /**
    * Value that identifies the version of this class that has been Serialized.
    */
    private static final long serialVersionUID = 1L;
    /** Hold attributes in a Map. TODO: ConcurrentHashMap may be better for multiple writers */
    protected Map m_attributes = new ConcurrentHashMap();
    /**
     * Contains the tool id related to this session if there is one
     */
    protected String m_tool_id = null;
    /**
     * Contains the context id related to this session if there is one
     */
    protected String m_context_id = null;
    /** 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;
    /**
    * 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;

    public MyLittleSession(String type, SessionManager sessionManager, String id, Session s, String littleId,
            ThreadLocalManager threadLocalManager, SessionAttributeListener sessionListener,
            SessionStore sessionStore, NonPortableSession nonPortableSession) {
        this.m_type = type;
        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);
    }

    /**
     * the session type of this session (tool OR context)
     */
    public String getSessionType() {
        return m_type;
    }

    /**
     * the tool id related to this session OR null if there is not one
     */
    public String getSessionToolId() {
        return m_tool_id;
    }

    /**
     * Set the current tool id
     * @param m_tool_id
     */
    public void setSessionToolId(String m_tool_id) {
        this.m_tool_id = m_tool_id;
    }

    /**
     * the tool id related to this session OR null if there is not one
     */
    public String getSessionContextId() {
        return m_context_id;
    }

    /**
     * Set the current context id
     * @param m_context_id
     */
    public void setSessionContextId(String m_context_id) {
        this.m_context_id = m_context_id;
    }

    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);
            }

            // allow the tool sessions to be more easily identified
            if (this.m_tool_id == null && TYPE_TOOL.equals(m_type) && sessionStore instanceof SessionComponent) {
                String toolId = ((SessionComponent) sessionStore).identifyCurrentTool();
                if (toolId != null) {
                    this.m_tool_id = toolId;
                }
            }

            // 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 int getMaxInactiveInterval() {
        return m_session.getMaxInactiveInterval();
    }

    /**
     * @inheritDoc
     */
    public void setMaxInactiveInterval(int arg0) {
        // TODO: just ignore this ?
    }

    /**
     * @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;
    }

    @Override
    public String toString() {
        return "MLS_" + m_type + (m_tool_id != null ? "(" + m_tool_id + ")" : "")
                + (m_context_id != null ? "[" + m_context_id + "]" : "") + "{at="
                + (m_attributes != null ? m_attributes.size() : 0) + ", id=" + m_id + ", ctx='" + m_littleId + '\''
                + ", " + (m_created > 0 ? new Date(m_created) : "?") + '}';
    }

}