// JigsawHttpSession.java
// $Id: JigsawHttpSession.java,v 1.5 1998/06/08 12:06:30 bmahe Exp $
// (c) COPYRIGHT MIT and INRIA, 1998.
// Please first read the full copyright statement in file COPYRIGHT.html
 
package org.w3c.jigsaw.servlet;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.*;
import java.util.*;

/**
 * @version $Revision: 1.5 $
 * @author  Benoît Mahé (bmahe@w3.org)
 */
public class JigsawHttpSession implements HttpSession {

    private JigsawHttpSessionContext sc = null;

    private String id = null;

    private long creationTime     = -1;
    private long lastAccessedTime = -1;

    private boolean isValid = false;
    private boolean isNew   = false;

    private Cookie cookie = null;

    private Hashtable values = null;

    /**
     * Returns the identifier assigned to this session. An HttpSession's 
     * identifier is a unique string that is created and maintained by
     * HttpSessionContext. 
     * @return the identifier assigned to this session 
     * @exception IllegalStateException if an attempt is made to access 
     * session data after the session has been invalidated 
     */
    public String getId() {
	return id;
    }

    /**
     * Returns the context in which this session is bound. 
     * @return the context in which this session is bound.
     * @exception IllegalStateException if an attempt is made to access 
     * session data after the session has been invalidated 
     */
    public HttpSessionContext getSessionContext() {
	return sc;
    }

    /**
     * Returns the time at which this session representation was created, 
     * in milliseconds since midnight, January 1, 1970 UTC. 
     * @return the time when the session was created 
     * @exception IllegalStateException if an attempt is made to access 
     * session data after the session has been invalidated 
     */
    public long getCreationTime() {
	return creationTime;
    }

    /**
     * Returns the last time the client sent a request carrying the identifier
     * assigned to the session. Time is expressed as milliseconds
     * since midnight, January 1, 1970 UTC. Application level operations, 
     * such as getting or setting a value associated with the session,
     * does not affect the access time. 
     * @return the last time the client sent a request carrying the identifier
     * assigned to the session 
     * @exception IllegalStateException if an attempt is made to access 
     * session data after the session has been invalidated 
     */
    public long getLastAccessedTime() {
	return lastAccessedTime;
    }

    protected void setLastAccessedTime() {
	lastAccessedTime = System.currentTimeMillis();
    }

    /**
     * Causes this representation of the session to be invalidated and removed
     * from its context. 
     * @exception IllegalStateException if an attempt is made to access 
     * session data after the session has been invalidated 
     */
    public void invalidate() {
	isValid = false;
	sc.removeSession(id);
    }

    /**
     * Returns the object bound to the given name in the session's application
     * layer data. Returns null if there is no such binding. 
     * @param name - the name of the binding to find 
     * @return the value bound to that name, or null if the binding does 
     * not exist. 
     * @exception IllegalStateException if an attempt is made to access 
     * session data after the session has been invalidated 
     */    
    public Object getValue(String name) {
	if (!isValid)
            throw new IllegalStateException("Invalid session");
	return values.get(name);
    }

    /**
     * Binds the specified object into the session's application layer data 
     * with the given name. Any existing binding with the same name
     * is replaced. New (or existing) values that implement the 
     * HttpSessionBindingListener interface will call its valueBound() method. 
     * @param name - the name to which the data object will be bound. 
     * This parameter cannot be null. 
     * @param value - the data object to be bound. This parameter cannot 
     * be null. 
     * @exception IllegalStateException if an attempt is made to access 
     * session data after the session has been invalidated 
     */
    public void putValue(String name, Object value)
    {
	if (!isValid)
            throw new IllegalStateException("Invalid session");
	removeValue(name);
	values.put(name, value);
	if (value instanceof HttpSessionBindingListener)
	    valueBound((HttpSessionBindingListener)value, name);
    }

    /**
     * Removes the object bound to the given name in the session's application
     * layer data. Does nothing if there is no object bound to the
     * given name. The value that implements the HttpSessionBindingListener 
     * interface will call its valueUnbound() method. 
     * @param name - the name of the object to remove 
     * @exception IllegalStateException if an attempt is made to access 
     * session data after the session has been invalidated 
     */
    public void removeValue(String name) {
	if (!isValid)
            throw new IllegalStateException("Invalid session");
	Object value = values.get(name);
	if (value != null) {
	    values.remove(name);
	    if (value instanceof HttpSessionBindingListener)
		valueUnbound((HttpSessionBindingListener)value, name);
	}
    }

    protected void valueBound(HttpSessionBindingListener value, String name) 
    {
	value.valueBound(new HttpSessionBindingEvent(this, name));
    }

    protected void valueUnbound(HttpSessionBindingListener value, String name) 
    {
	value.valueUnbound(new HttpSessionBindingEvent(this, name));
    }
    
    /**
     * Returns an array of the names of all the application layer data objects
     * bound into the session. For example, if you want to delete
     * all of the data objects bound into the session, use this method to 
     * obtain their names. 
     * @return an array containing the names of all of the application layer
     * data objects bound into the session 
     * @exception IllegalStateException if an attempt is made to access 
     * session data after the session has been invalidated 
     */
    public String[] getValueNames() {
	if (!isValid)
            throw new IllegalStateException("Invalid session");
	String names[] = new String[values.size()];
	Enumeration enum = values.keys();
	int i = 0;
	while (enum.hasMoreElements())
	    names[i++] = (String)enum.nextElement();
	return names;
    }

    /**
     * A session is considered to be "new" if it has been created by the 
     * server, but the client has not yet acknowledged joining the
     * session. For example, if the server supported only cookie-based 
     * sessions and the client had completely disabled the use of
     * cookies, then calls to HttpServletRequest.getSession() would always 
     * return "new" sessions. 
     * @return true if the session has been created by the server but the 
     * client has not yet acknowledged joining the session; false otherwise 
     * @exception IllegalStateException if an attempt is made to access 
     * session data after the session has been invalidated 
     */
    public boolean isNew() {
	return isNew;
    }

    protected void setNoMoreNew() {
	isNew = false;
    }

    protected boolean isValid() {
        return isValid;
    }

    protected Cookie getCookie() {
        return cookie;
    }

    public JigsawHttpSession(JigsawHttpSessionContext context, Cookie cookie) {
	this.values = new Hashtable();
	this.creationTime = System.currentTimeMillis();
	this.lastAccessedTime = creationTime;
	this.sc = context;
	this.id = context.addSession(this);
	this.cookie = cookie;
	cookie.setValue(this.id);
	isValid = true;
	isNew = true;
    }

}
