/*  Copyright (c) 2000 by Oracle Corporation. All Rights Reserved. */
package oracle.portal.provider.v2.http;

import java.io.Serializable;
import javax.servlet.http.*;
import java.util.*;
import oracle.portal.provider.v2.*;

/**
 * Servlet specific implementation of the {@link
 * oracle.portal.provider.v2.ProviderSession} interface.
 */
public class ServletProviderSession
    implements ProviderSession, HttpSessionBindingListener,
    HttpSessionActivationListener, Serializable
{
    public static final String SESSION_ID =
        "oracle.portal.provider.http.v2.ServletProviderSession";
    private static Object sLock = new Object();

    // Make this transient, as there is no hope of serializing it. We restore
    // the session pointer after migration using the ActivationListener events
    // instead
    private transient HttpSession mSession = null;

    public static ServletProviderSession getAttachedSession(HttpServletRequest request)
    {
        HttpSession httpSession = request.getSession(false);
        if (httpSession != null)
        {
            try
            {
                return (ServletProviderSession)httpSession.getAttribute(ServletProviderSession.SESSION_ID);
            }
            catch (IllegalStateException ise)
            {
                return null;
            }
        }
        else
        {
            return null;
        }
    }

    public static ServletProviderSession getAttachedSession(HttpServletRequest request,
                                                            boolean create)
    {
        ServletProviderSession session = getAttachedSession(request);
        if (session == null && create)
        {
            synchronized(sLock)
            {
                session = getAttachedSession(request);
                if (session == null)
                {
                    session = new ServletProviderSession(request);
                    session.setAttribute(SESSION_ID, session);
                }
            }
        }
        return session;
    }

    // Private constructor only to be called from getAttachedSession methods.
    private ServletProviderSession(HttpServletRequest request)
    {
        // Create the http session if it doesn't already exist and store a
        // reference to it.
        mSession = request.getSession(true);
    }

    /**
     * Returns the time when this session was created, measured in milliseconds
     * since midnight January 1, 1970 GMT.
     *
     * @return a <code>long</code> specifying when this session was created,
     * expressed in milliseconds since 1/1/1970 GMT.
     * @exception IllegalStateException if this method is called on an
     * invalidated session
     */
    public long getCreationTime() throws IllegalStateException
    {
        if (mSession == null)
        {
            throw new IllegalStateException("Session has expired.");
        }
        return mSession.getCreationTime();
    }

    /**
     * Returns a string containing the unique identifier assigned to this
     * session. The identifier is assigned by the servlet container and is
     * implementation dependent.
     * 
     * @return a string specifying the identifier assigned to this session.
     * @exception IllegalStateException if this method is called on an
     * invalidated session
     */
    public String getId()  throws IllegalStateException
    {
        if (mSession == null)
        {
            throw new IllegalStateException("Session has expired.");
        }
        return mSession.getId();
    }

    /**
     * Returns the last time the client sent a request associated with this
     * session, as the number of milliseconds since midnight January 1, 1970
     * GMT.<p>
     *
     * Actions that your application takes, such as getting or setting a value
     * associated with the session, do not affect the access time.
     *
     * @return a <code>long</code> representing the last time the client sent a
     * request associated with this session, expressed in milliseconds since
     * 1/1/1970 GMT
     * @exception IllegalStateException if this method is called on an
     * invalidated session
     */
    public long getLastAccessedTime()  throws IllegalStateException
    {
        if (mSession == null)
        {
            throw new IllegalStateException("Session has expired.");
        }
        return mSession.getLastAccessedTime();
    }

   /**
    * Returns the maximum time interval, in seconds, that the servlet engine
    * will keep this session open between client requests. You can set the
    * maximum time interval with the <code>setMaxInactiveInterval</code>
    * method.
    *
    * @return an integer specifying the number of seconds this session
    * remains open between client requests.
    */
    public int getMaxInactiveInterval()
    {
        if (mSession == null)
        {
            throw new IllegalStateException("Session has expired.");
        }
        return mSession.getMaxInactiveInterval();
    }
    
    /**
     * Returns the object bound with the specified name in this session, or
     * <code>null</code> if no object is bound under the name.
     *
     * @param name a string specifying the name of the object
     * @return the object with the specified name
     * @exception IllegalStateException if this method is called on an
     * invalidated session
     */
    public Object getAttribute(String name) throws IllegalStateException
    {
        if (mSession == null)
        {
            throw new IllegalStateException("Session has expired.");
        }
        return mSession.getAttribute(name);
    }
    
    /**
     * Returns an <code>Enumeration</code> of <code>String</code> objects
     * containing the names of all the objects bound to this session.
     *
     * @return an <code>Enumeration</code> of <code>String</code> objects
     * specifying the names of all the objects bound to this session
     * @exception IllegalStateException if this method is called on an
     * invalidated session
     */
    public Enumeration getAttributeNames() throws IllegalStateException
    {
        if (mSession == null)
        {
            throw new IllegalStateException("Session has expired.");
        }
        return mSession.getAttributeNames();
    }

    /**
     * Binds an object to this session, using the name specified.  If an object
     * of the same name is already bound to the session, the object is
     * replaced.<p>
     *
     * After this method executes, and if the object implements
     * <code>HttpSessionBindingListener</code>, the container calls
     * <code>HttpSessionBindingListener.valueBound</code>.
     *
     * @param name the name to which the object is bound; cannot be null
     * @param value the object to be bound; cannot be null
     * @exception IllegalStateException if this method is called on an
     * invalidated session
     */
    public void setAttribute(String name, Object value)
        throws IllegalStateException
    {
        if (mSession == null)
        {
            throw new IllegalStateException("Session has expired.");
        }
        mSession.setAttribute(name, value);
    }
    
    /**
     * Removes the object bound with the specified name from this session. If
     * the session does not have an object bound with the specified name, this
     * method does nothing.<p>
     *
     * After this method executes, and if the object implements
     * <code>HttpSessionBindingListener</code>, the container calls
     * <code>HttpSessionBindingListener.valueUnbound</code>.
     *
     * @param name the name of the object to remove from this session.
     * @exception IllegalStateException if this method is called on an
     * invalidated session
     */
    public void removeAttribute(String name) throws IllegalStateException
    {
        if (mSession == null)
        {
            throw new IllegalStateException("Session has expired.");
        }
        mSession.removeAttribute(name);
    }

    /**
     * Invalidates this session and unbinds any objects bound to it.
     *
     * @exception IllegalStateException if this method is called on an
     * already invalidated session
     */
    public void invalidate() throws IllegalStateException
    {
        if (mSession == null)
        {
            throw new IllegalStateException("Session has expired.");
        }
        mSession.invalidate();
    }
    
    /**
     * Returns <code>true</code> if the client does not yet know about the
     * session or if the client chooses not to join the session.  For example,
     * if the server used only cookie-based sessions, and the client had
     * disabled the use of cookies, then a session would be new on each
     * request.
     *
     * @return <code>true</code> if the server has created a session, but the
     * client has not yet joined
     * @exception IllegalStateException if this method is called on an already
     * invalidated session
     */
    public boolean isNew() throws IllegalStateException
    {
        if (mSession == null)
        {
            throw new IllegalStateException("Session has expired.");
        }
        return mSession.isNew();
    }

    public void valueBound(HttpSessionBindingEvent event)
    {
        // Do nothing -- we already now our session
    }

    /**
     * In case the User is attached to a session we want to be able to remove
     * its session reference so the http session can ultimately be garbage
     * collected.
     */
    public void valueUnbound(HttpSessionBindingEvent event)
    {
        // Remove our reference to the session so it can ultimately be freed
        mSession = null;
    }

    /**
     * Notification that the session is about to be passivated.
     */
    public void sessionWillPassivate(HttpSessionEvent se)
    {
    }

    /**
     * Notification that the session has just been activated.
     */
    public void sessionDidActivate(HttpSessionEvent se)
    {
        // Use the activation event to get a pointer to the session we migrated
        // to
        mSession = se.getSession();
    }

}

