Coverage Report - org.apache.commons.messagelet.impl.HttpSessionImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
HttpSessionImpl
0%
0/256
0%
0/82
2.133
 
 1  
 /*
 2  
  * Copyright 1999,2004 The Apache Software Foundation.
 3  
  * 
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  * 
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  * 
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 
 17  
 
 18  
 package org.apache.commons.messagelet.impl;
 19  
 
 20  
 
 21  
 import java.beans.PropertyChangeSupport;
 22  
 import java.io.IOException;
 23  
 import java.io.NotSerializableException;
 24  
 import java.io.ObjectInputStream;
 25  
 import java.io.ObjectOutputStream;
 26  
 import java.io.Serializable;
 27  
 import java.security.Principal;
 28  
 import java.util.ArrayList;
 29  
 import java.util.Enumeration;
 30  
 import java.util.HashMap;
 31  
 import java.util.Iterator;
 32  
 
 33  
 import javax.servlet.ServletContext;
 34  
 import javax.servlet.http.HttpSession;
 35  
 import javax.servlet.http.HttpSessionActivationListener;
 36  
 import javax.servlet.http.HttpSessionBindingEvent;
 37  
 import javax.servlet.http.HttpSessionBindingListener;
 38  
 import javax.servlet.http.HttpSessionContext;
 39  
 import javax.servlet.http.HttpSessionEvent;
 40  
 
 41  
 import org.apache.commons.collections.iterators.IteratorEnumeration;
 42  
 
 43  
 /**
 44  
  * Based on the Catalina StandardSession class.
 45  
  * Standard implementation of the <b>HttpSession</b> interface.  This object is
 46  
  * serializable, so that it can be stored in persistent storage or transferred
 47  
  * to a different JVM for distributable session support.
 48  
  * <p>
 49  
  *
 50  
  * @author Craig R. McClanahan
 51  
  * @author Sean Legassick
 52  
  * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
 53  
  * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
 54  
  * @version $Revision: 155459 $ $Date: 2005-02-26 13:24:44 +0000 (Sat, 26 Feb 2005) $
 55  
  */
 56  
 
 57  
 public class HttpSessionImpl implements HttpSession, Serializable {
 58  
 
 59  
 
 60  
 
 61  
     // ----------------------------------------------------- Instance Variables
 62  
 
 63  
 
 64  
     /**
 65  
      * The dummy attribute value serialized when a NotSerializableException is
 66  
      * encountered in <code>writeObject()</code>.
 67  
      */
 68  
     private static final String NOT_SERIALIZED =
 69  
         "___NOT_SERIALIZABLE_EXCEPTION___";
 70  
 
 71  
 
 72  
     /**
 73  
      * The collection of user data attributes associated with this Session.
 74  
      */
 75  0
     private HashMap attributes = new HashMap();
 76  
 
 77  
 
 78  
     /**
 79  
      * The authentication type used to authenticate our cached Principal,
 80  
      * if any.  NOTE:  This value is not included in the serialized
 81  
      * version of this object.
 82  
      */
 83  0
     private transient String authType = null;
 84  
 
 85  
 
 86  
     /**
 87  
      * The time this session was created, in milliseconds since midnight,
 88  
      * January 1, 1970 GMT.
 89  
      */
 90  0
     private long creationTime = 0L;
 91  
 
 92  
 
 93  
     /**
 94  
      * The debugging detail level for this component.  NOTE:  This value
 95  
      * is not included in the serialized version of this object.
 96  
      */
 97  0
     private transient int debug = 0;
 98  
 
 99  
 
 100  
     /**
 101  
      * We are currently processing a session expiration, so bypass
 102  
      * certain IllegalStateException tests.  NOTE:  This value is not
 103  
      * included in the serialized version of this object.
 104  
      */
 105  0
     private transient boolean expiring = false;
 106  
 
 107  
 
 108  
     /**
 109  
      * The session identifier of this Session.
 110  
      */
 111  0
     private String id = null;
 112  
 
 113  
 
 114  
 
 115  
     /**
 116  
      * The last accessed time for this Session.
 117  
      */
 118  0
     private long lastAccessedTime = creationTime;
 119  
 
 120  
 
 121  
     /**
 122  
      * The session event listeners for this Session.
 123  
      */
 124  0
     private transient ArrayList listeners = new ArrayList();
 125  
 
 126  
 
 127  
 
 128  
     /**
 129  
      * The maximum time interval, in seconds, between client requests before
 130  
      * the servlet container may invalidate this session.  A negative time
 131  
      * indicates that the session should never time out.
 132  
      */
 133  0
     private int maxInactiveInterval = -1;
 134  
 
 135  
 
 136  
     /**
 137  
      * Flag indicating whether this session is new or not.
 138  
      */
 139  0
     private boolean isNew = false;
 140  
 
 141  
 
 142  
     /**
 143  
      * Flag indicating whether this session is valid or not.
 144  
      */
 145  0
     private boolean isValid = false;
 146  
 
 147  
 
 148  
     /**
 149  
      * Internal notes associated with this session by Catalina components
 150  
      * and event listeners.  <b>IMPLEMENTATION NOTE:</b> This object is
 151  
      * <em>not</em> saved and restored across session serializations!
 152  
      */
 153  0
     private transient HashMap notes = new HashMap();
 154  
 
 155  
 
 156  
     /**
 157  
      * The authenticated Principal associated with this session, if any.
 158  
      * <b>IMPLEMENTATION NOTE:</b>  This object is <i>not</i> saved and
 159  
      * restored across session serializations!
 160  
      */
 161  0
     private transient Principal principal = null;
 162  
 
 163  
 
 164  
     /**
 165  
      * The property change support for this component.  NOTE:  This value
 166  
      * is not included in the serialized version of this object.
 167  
      */
 168  0
     private transient PropertyChangeSupport support =
 169  
         new PropertyChangeSupport(this);
 170  
 
 171  
 
 172  
     /**
 173  
      * The current accessed time for this session.
 174  
      */
 175  0
     private long thisAccessedTime = creationTime;
 176  
 
 177  
     /**
 178  
      * The ServletContext 
 179  
      */
 180  
     protected ServletContext servletContext;
 181  
 
 182  
     /**
 183  
      * Is this session distributable. If so then 
 184  
      * its values must be Serializable.
 185  
      */
 186  0
     private boolean distributable = false;
 187  
     
 188  
     
 189  0
     public HttpSessionImpl(ServletContext servletContext) {
 190  0
         this.servletContext = servletContext;
 191  0
     }
 192  
 
 193  
     // ----------------------------------------------------- Session Properties
 194  
 
 195  
 
 196  
     /**
 197  
      * Return the authentication type used to authenticate our cached
 198  
      * Principal, if any.
 199  
      */
 200  
     public String getAuthType() {
 201  
 
 202  0
         return (this.authType);
 203  
 
 204  
     }
 205  
 
 206  
 
 207  
     /**
 208  
      * Set the authentication type used to authenticate our cached
 209  
      * Principal, if any.
 210  
      *
 211  
      * @param authType The new cached authentication type
 212  
      */
 213  
     public void setAuthType(String authType) {
 214  
 
 215  0
         String oldAuthType = this.authType;
 216  0
         this.authType = authType;
 217  0
         support.firePropertyChange("authType", oldAuthType, this.authType);
 218  
 
 219  0
     }
 220  
 
 221  
 
 222  
     /**
 223  
      * Set the creation time for this session.  This method is called by the
 224  
      * Manager when an existing Session instance is reused.
 225  
      *
 226  
      * @param time The new creation time
 227  
      */
 228  
     public void setCreationTime(long time) {
 229  
 
 230  0
         this.creationTime = time;
 231  0
         this.lastAccessedTime = time;
 232  0
         this.thisAccessedTime = time;
 233  
 
 234  0
     }
 235  
 
 236  
 
 237  
     /**
 238  
      * Return the session identifier for this session.
 239  
      */
 240  
     public String getId() {
 241  
 
 242  0
         return (this.id);
 243  
 
 244  
     }
 245  
 
 246  
 
 247  
     /**
 248  
      * Set the session identifier for this session.
 249  
      *
 250  
      * @param id The new session identifier
 251  
      */
 252  
     public void setId(String id) {
 253  
 
 254  0
         this.id = id;
 255  
 
 256  
         // Notify interested session event listeners
 257  
         ///fireSessionEvent(Session.SESSION_CREATED_EVENT, null);
 258  
 
 259  0
     }
 260  
 
 261  
 
 262  
     /**
 263  
      * Return the last time the client sent a request associated with this
 264  
      * session, as the number of milliseconds since midnight, January 1, 1970
 265  
      * GMT.  Actions that your application takes, such as getting or setting
 266  
      * a value associated with the session, do not affect the access time.
 267  
      */
 268  
     public long getLastAccessedTime() {
 269  
 
 270  0
         return (this.lastAccessedTime);
 271  
 
 272  
     }
 273  
 
 274  
 
 275  
     /**
 276  
      * Return the maximum time interval, in seconds, between client requests
 277  
      * before the servlet container will invalidate the session.  A negative
 278  
      * time indicates that the session should never time out.
 279  
      *
 280  
      * @exception IllegalStateException if this method is called on
 281  
      *  an invalidated session
 282  
      */
 283  
     public int getMaxInactiveInterval() {
 284  
 
 285  0
         if (!isValid) {
 286  0
             throw new IllegalStateException( "Cannot call getMaxInactiveInterval on an invalidated session" );
 287  
         }
 288  
 
 289  0
         return (this.maxInactiveInterval);
 290  
 
 291  
     }
 292  
 
 293  
 
 294  
     /**
 295  
      * Set the maximum time interval, in seconds, between client requests
 296  
      * before the servlet container will invalidate the session.  A negative
 297  
      * time indicates that the session should never time out.
 298  
      *
 299  
      * @param interval The new maximum interval
 300  
      */
 301  
     public void setMaxInactiveInterval(int interval) {
 302  
 
 303  0
         this.maxInactiveInterval = interval;
 304  
 
 305  0
     }
 306  
 
 307  
 
 308  
     /**
 309  
      * Set the <code>isNew</code> flag for this session.
 310  
      *
 311  
      * @param isNew The new value for the <code>isNew</code> flag
 312  
      */
 313  
     public void setNew(boolean isNew) {
 314  
 
 315  0
         this.isNew = isNew;
 316  
 
 317  0
     }
 318  
 
 319  
 
 320  
     /**
 321  
      * Return the authenticated Principal that is associated with this Session.
 322  
      * This provides an <code>Authenticator</code> with a means to cache a
 323  
      * previously authenticated Principal, and avoid potentially expensive
 324  
      * <code>Realm.authenticate()</code> calls on every request.  If there
 325  
      * is no current associated Principal, return <code>null</code>.
 326  
      */
 327  
     public Principal getPrincipal() {
 328  
 
 329  0
         return (this.principal);
 330  
 
 331  
     }
 332  
 
 333  
 
 334  
     /**
 335  
      * Set the authenticated Principal that is associated with this Session.
 336  
      * This provides an <code>Authenticator</code> with a means to cache a
 337  
      * previously authenticated Principal, and avoid potentially expensive
 338  
      * <code>Realm.authenticate()</code> calls on every request.
 339  
      *
 340  
      * @param principal The new Principal, or <code>null</code> if none
 341  
      */
 342  
     public void setPrincipal(Principal principal) {
 343  
 
 344  0
         Principal oldPrincipal = this.principal;
 345  0
         this.principal = principal;
 346  0
         support.firePropertyChange("principal", oldPrincipal, this.principal);
 347  
 
 348  0
     }
 349  
 
 350  
 
 351  
 
 352  
     /**
 353  
      * Return the <code>isValid</code> flag for this session.
 354  
      */
 355  
     public boolean isValid() {
 356  
 
 357  0
         return (this.isValid);
 358  
 
 359  
     }
 360  
 
 361  
 
 362  
     /**
 363  
      * Set the <code>isValid</code> flag for this session.
 364  
      *
 365  
      * @param isValid The new value for the <code>isValid</code> flag
 366  
      */
 367  
     public void setValid(boolean isValid) {
 368  
 
 369  0
         this.isValid = isValid;
 370  0
     }
 371  
 
 372  
 
 373  
     // ------------------------------------------------- Session Public Methods
 374  
 
 375  
 
 376  
     /**
 377  
      * Update the accessed time information for this session.  This method
 378  
      * should be called by the context when a request comes in for a particular
 379  
      * session, even if the application does not reference it.
 380  
      */
 381  
     public void access() {
 382  
 
 383  0
         this.isNew = false;
 384  0
         this.lastAccessedTime = this.thisAccessedTime;
 385  0
         this.thisAccessedTime = System.currentTimeMillis();
 386  
 
 387  0
     }
 388  
 
 389  
 
 390  
 
 391  
     /**
 392  
      * Perform the internal processing required to invalidate this session,
 393  
      * without triggering an exception if the session has already expired.
 394  
      */
 395  
     public void expire() {
 396  
 
 397  
         // Mark this session as "being expired" if needed
 398  0
         if (expiring)
 399  0
             return;
 400  0
         expiring = true;
 401  0
         setValid(false);
 402  
 
 403  
         // Unbind any objects associated with this session
 404  0
         String keys[] = keys();
 405  0
         for (int i = 0; i < keys.length; i++)
 406  0
             removeAttribute(keys[i]);
 407  
 
 408  
         // Notify interested session event listeners
 409  
         //fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null);
 410  
 
 411  
         // We have completed expire of this session
 412  0
         expiring = false;
 413  0
     }
 414  
 
 415  
 
 416  
     /**
 417  
      * Perform the internal processing required to passivate
 418  
      * this session.
 419  
      */
 420  
     public void passivate() {
 421  
 
 422  
         // Notify ActivationListeners
 423  0
         HttpSessionEvent event = null;
 424  0
         String keys[] = keys();
 425  0
         for (int i = 0; i < keys.length; i++) {
 426  0
             Object attribute = getAttribute(keys[i]);
 427  0
             if (attribute instanceof HttpSessionActivationListener) {
 428  0
                 if (event == null)
 429  0
                     event = new HttpSessionEvent(this);
 430  
                 // FIXME: Should we catch throwables?
 431  0
                 ((HttpSessionActivationListener)attribute).sessionWillPassivate(event);
 432  
             }
 433  
         }
 434  
 
 435  0
     }
 436  
 
 437  
 
 438  
     /**
 439  
      * Perform internal processing required to activate this
 440  
      * session.
 441  
      */
 442  
     public void activate() {
 443  
 
 444  
         // Notify ActivationListeners
 445  0
         HttpSessionEvent event = null;
 446  0
         String keys[] = keys();
 447  0
         for (int i = 0; i < keys.length; i++) {
 448  0
             Object attribute = getAttribute(keys[i]);
 449  0
             if (attribute instanceof HttpSessionActivationListener) {
 450  0
                 if (event == null)
 451  0
                     event = new HttpSessionEvent(this);
 452  
                 // FIXME: Should we catch throwables?
 453  0
                 ((HttpSessionActivationListener)attribute).sessionDidActivate(event);
 454  
             }
 455  
         }
 456  
 
 457  0
     }
 458  
 
 459  
 
 460  
     /**
 461  
      * Return the object bound with the specified name to the internal notes
 462  
      * for this session, or <code>null</code> if no such binding exists.
 463  
      *
 464  
      * @param name Name of the note to be returned
 465  
      */
 466  
     public Object getNote(String name) {
 467  
 
 468  0
         synchronized (notes) {
 469  0
             return (notes.get(name));
 470  0
         }
 471  
 
 472  
     }
 473  
 
 474  
 
 475  
     /**
 476  
      * Return an Iterator containing the String names of all notes bindings
 477  
      * that exist for this session.
 478  
      */
 479  
     public Iterator getNoteNames() {
 480  
 
 481  0
         synchronized (notes) {
 482  0
             return (notes.keySet().iterator());
 483  0
         }
 484  
 
 485  
     }
 486  
 
 487  
 
 488  
     /**
 489  
      * Release all object references, and initialize instance variables, in
 490  
      * preparation for reuse of this object.
 491  
      */
 492  
     public void recycle() {
 493  
 
 494  
         // Reset the instance variables associated with this Session
 495  0
         attributes.clear();
 496  0
         setAuthType(null);
 497  0
         creationTime = 0L;
 498  0
         expiring = false;
 499  0
         id = null;
 500  0
         lastAccessedTime = 0L;
 501  0
         maxInactiveInterval = -1;
 502  0
         setPrincipal(null);
 503  0
         isNew = false;
 504  0
         isValid = false;
 505  0
     }
 506  
 
 507  
 
 508  
     /**
 509  
      * Remove any object bound to the specified name in the internal notes
 510  
      * for this session.
 511  
      *
 512  
      * @param name Name of the note to be removed
 513  
      */
 514  
     public void removeNote(String name) {
 515  
 
 516  0
         synchronized (notes) {
 517  0
             notes.remove(name);
 518  0
         }
 519  
 
 520  0
     }
 521  
 
 522  
 
 523  
 
 524  
     /**
 525  
      * Bind an object to a specified name in the internal notes associated
 526  
      * with this session, replacing any existing binding for this name.
 527  
      *
 528  
      * @param name Name to which the object should be bound
 529  
      * @param value Object to be bound to the specified name
 530  
      */
 531  
     public void setNote(String name, Object value) {
 532  
 
 533  0
         synchronized (notes) {
 534  0
             notes.put(name, value);
 535  0
         }
 536  
 
 537  0
     }
 538  
 
 539  
 
 540  
     /**
 541  
      * Return a string representation of this object.
 542  
      */
 543  
     public String toString() {
 544  
 
 545  0
         StringBuffer sb = new StringBuffer();
 546  0
         sb.append("StandardSession[");
 547  0
         sb.append(id);
 548  0
         sb.append("]");
 549  0
         return (sb.toString());
 550  
 
 551  
     }
 552  
 
 553  
 
 554  
     // ------------------------------------------------ Session Package Methods
 555  
 
 556  
 
 557  
     /**
 558  
      * Read a serialized version of the contents of this session object from
 559  
      * the specified object input stream, without requiring that the
 560  
      * StandardSession itself have been serialized.
 561  
      *
 562  
      * @param stream The object input stream to read from
 563  
      *
 564  
      * @exception ClassNotFoundException if an unknown class is specified
 565  
      * @exception IOException if an input/output error occurs
 566  
      */
 567  
     void readObjectData(ObjectInputStream stream)
 568  
         throws ClassNotFoundException, IOException {
 569  
 
 570  0
         readObject(stream);
 571  
 
 572  0
     }
 573  
 
 574  
 
 575  
     /**
 576  
      * Write a serialized version of the contents of this session object to
 577  
      * the specified object output stream, without requiring that the
 578  
      * StandardSession itself have been serialized.
 579  
      *
 580  
      * @param stream The object output stream to write to
 581  
      *
 582  
      * @exception IOException if an input/output error occurs
 583  
      */
 584  
     void writeObjectData(ObjectOutputStream stream)
 585  
         throws IOException {
 586  
 
 587  0
         writeObject(stream);
 588  
 
 589  0
     }
 590  
 
 591  
 
 592  
     // ------------------------------------------------- HttpSession Properties
 593  
 
 594  
 
 595  
     /**
 596  
      * Return the time when this session was created, in milliseconds since
 597  
      * midnight, January 1, 1970 GMT.
 598  
      *
 599  
      * @exception IllegalStateException if this method is called on an
 600  
      *  invalidated session
 601  
      */
 602  
     public long getCreationTime() {
 603  
 
 604  0
         if (!isValid) {
 605  0
             throw new IllegalStateException( "Cannot call getCreationTime() on invalidated session" );
 606  
         }
 607  
 
 608  0
         return (this.creationTime);
 609  
 
 610  
     }
 611  
 
 612  
 
 613  
     /**
 614  
      * Return the ServletContext to which this session belongs.
 615  
      */
 616  
     public ServletContext getServletContext() {
 617  0
         return servletContext;
 618  
 
 619  
     }
 620  
 
 621  
 
 622  
     /**
 623  
      * Return the session context with which this session is associated.
 624  
      *
 625  
      * @deprecated As of Version 2.1, this method is deprecated and has no
 626  
      *  replacement.  It will be removed in a future version of the
 627  
      *  Java Servlet API.
 628  
      */
 629  
     public HttpSessionContext getSessionContext() {
 630  
 
 631  0
         return null;
 632  
 
 633  
     }
 634  
 
 635  
 
 636  
     // ----------------------------------------------HttpSession Public Methods
 637  
 
 638  
 
 639  
     /**
 640  
      * Return the object bound with the specified name in this session, or
 641  
      * <code>null</code> if no object is bound with that name.
 642  
      *
 643  
      * @param name Name of the attribute to be returned
 644  
      *
 645  
      * @exception IllegalStateException if this method is called on an
 646  
      *  invalidated session
 647  
      */
 648  
     public Object getAttribute(String name) {
 649  
 
 650  0
         if (!isValid) {
 651  0
             throw new IllegalStateException( "Cannot call getAttribute() on invalidated session" );
 652  
         }
 653  
 
 654  0
         synchronized (attributes) {
 655  0
             return (attributes.get(name));
 656  0
         }
 657  
 
 658  
     }
 659  
 
 660  
 
 661  
     /**
 662  
      * Return an <code>Enumeration</code> of <code>String</code> objects
 663  
      * containing the names of the objects bound to this session.
 664  
      *
 665  
      * @exception IllegalStateException if this method is called on an
 666  
      *  invalidated session
 667  
      */
 668  
     public Enumeration getAttributeNames() {
 669  
 
 670  0
         if (!isValid) {
 671  0
             throw new IllegalStateException( "Cannot call getAttributeNames() on invalidated session" );
 672  
         }
 673  
 
 674  0
         synchronized (attributes) {
 675  0
             return (new IteratorEnumeration(attributes.keySet().iterator()));
 676  0
         }
 677  
 
 678  
     }
 679  
 
 680  
 
 681  
     /**
 682  
      * Return the object bound with the specified name in this session, or
 683  
      * <code>null</code> if no object is bound with that name.
 684  
      *
 685  
      * @param name Name of the value to be returned
 686  
      *
 687  
      * @exception IllegalStateException if this method is called on an
 688  
      *  invalidated session
 689  
      *
 690  
      * @deprecated As of Version 2.2, this method is replaced by
 691  
      *  <code>getAttribute()</code>
 692  
      */
 693  
     public Object getValue(String name) {
 694  
 
 695  0
         return (getAttribute(name));
 696  
 
 697  
     }
 698  
 
 699  
 
 700  
     /**
 701  
      * Return the set of names of objects bound to this session.  If there
 702  
      * are no such objects, a zero-length array is returned.
 703  
      *
 704  
      * @exception IllegalStateException if this method is called on an
 705  
      *  invalidated session
 706  
      *
 707  
      * @deprecated As of Version 2.2, this method is replaced by
 708  
      *  <code>getAttributeNames()</code>
 709  
      */
 710  
     public String[] getValueNames() {
 711  
 
 712  0
         if (!isValid) {
 713  0
             throw new IllegalStateException( "Cannot call getValueNames() on invalidated session" );
 714  
         }
 715  
 
 716  0
         return (keys());
 717  
 
 718  
     }
 719  
 
 720  
 
 721  
     /**
 722  
      * Invalidates this session and unbinds any objects bound to it.
 723  
      *
 724  
      * @exception IllegalStateException if this method is called on
 725  
      *  an invalidated session
 726  
      */
 727  
     public void invalidate() {
 728  
 
 729  0
         if (!isValid) {
 730  0
             throw new IllegalStateException( "Cannot call invalidate() on invalidated session" );
 731  
         }
 732  
 
 733  
         // Cause this session to expire
 734  0
         expire();
 735  
 
 736  0
     }
 737  
 
 738  
 
 739  
     /**
 740  
      * Return <code>true</code> if the client does not yet know about the
 741  
      * session, or if the client chooses not to join the session.  For
 742  
      * example, if the server used only cookie-based sessions, and the client
 743  
      * has disabled the use of cookies, then a session would be new on each
 744  
      * request.
 745  
      *
 746  
      * @exception IllegalStateException if this method is called on an
 747  
      *  invalidated session
 748  
      */
 749  
     public boolean isNew() {
 750  
 
 751  0
         if (!isValid) {
 752  0
             throw new IllegalStateException( "Cannot call isNew() on invalidated session" );
 753  
         }
 754  
 
 755  0
         return (this.isNew);
 756  
 
 757  
     }
 758  
 
 759  
 
 760  
     /**
 761  
      * Bind an object to this session, using the specified name.  If an object
 762  
      * of the same name is already bound to this session, the object is
 763  
      * replaced.
 764  
      * <p>
 765  
      * After this method executes, and if the object implements
 766  
      * <code>HttpSessionBindingListener</code>, the container calls
 767  
      * <code>valueBound()</code> on the object.
 768  
      *
 769  
      * @param name Name to which the object is bound, cannot be null
 770  
      * @param value Object to be bound, cannot be null
 771  
      *
 772  
      * @exception IllegalStateException if this method is called on an
 773  
      *  invalidated session
 774  
      *
 775  
      * @deprecated As of Version 2.2, this method is replaced by
 776  
      *  <code>setAttribute()</code>
 777  
      */
 778  
     public void putValue(String name, Object value) {
 779  
 
 780  0
         setAttribute(name, value);
 781  
 
 782  0
     }
 783  
 
 784  
 
 785  
     /**
 786  
      * Remove the object bound with the specified name from this session.  If
 787  
      * the session does not have an object bound with this name, this method
 788  
      * does nothing.
 789  
      * <p>
 790  
      * After this method executes, and if the object implements
 791  
      * <code>HttpSessionBindingListener</code>, the container calls
 792  
      * <code>valueUnbound()</code> on the object.
 793  
      *
 794  
      * @param name Name of the object to remove from this session.
 795  
      *
 796  
      * @exception IllegalStateException if this method is called on an
 797  
      *  invalidated session
 798  
      */
 799  
     public void removeAttribute(String name) {
 800  
 
 801  
         // Validate our current state
 802  0
         if (!expiring && !isValid) { 
 803  0
             throw new IllegalStateException( "Cannot call removeAttribute() on an invalidated session" );
 804  
         }
 805  
 
 806  
         // Remove this attribute from our collection
 807  0
         Object value = null;
 808  0
         boolean found = false;
 809  0
         synchronized (attributes) {
 810  0
             found = attributes.containsKey(name);
 811  0
             if (found) {
 812  0
                 value = attributes.get(name);
 813  0
                 attributes.remove(name);
 814  
             } else {
 815  0
                 return;
 816  
             }
 817  0
         }
 818  
 /*
 819  
         // Call the valueUnbound() method if necessary
 820  
         HttpSessionBindingEvent event =
 821  
           new HttpSessionBindingEvent((HttpSession) this, name, value);
 822  
         if ((value != null) &&
 823  
             (value instanceof HttpSessionBindingListener))
 824  
             ((HttpSessionBindingListener) value).valueUnbound(event);
 825  
 
 826  
         // Notify interested application event listeners
 827  
         StandardContext context = (StandardContext) manager.getContainer();
 828  
         Object listeners[] = context.getApplicationListeners();
 829  
         if (listeners == null)
 830  
             return;
 831  
         for (int i = 0; i < listeners.length; i++) {
 832  
             if (!(listeners[i] instanceof HttpSessionAttributeListener))
 833  
                 continue;
 834  
             HttpSessionAttributeListener listener =
 835  
                 (HttpSessionAttributeListener) listeners[i];
 836  
             try {
 837  
                 context.fireContainerEvent("beforeSessionAttributeRemoved",
 838  
                                            listener);
 839  
                 listener.attributeRemoved(event);
 840  
                 context.fireContainerEvent("afterSessionAttributeRemoved",
 841  
                                            listener);
 842  
             } catch (Throwable t) {
 843  
                 context.fireContainerEvent("afterSessionAttributeRemoved",
 844  
                                            listener);
 845  
                 // FIXME - should we do anything besides log these?
 846  
                 log( "Exception firing attribute event", t);
 847  
             }
 848  
         }
 849  
 */
 850  0
     }
 851  
 
 852  
 
 853  
     /**
 854  
      * Remove the object bound with the specified name from this session.  If
 855  
      * the session does not have an object bound with this name, this method
 856  
      * does nothing.
 857  
      * <p>
 858  
      * After this method executes, and if the object implements
 859  
      * <code>HttpSessionBindingListener</code>, the container calls
 860  
      * <code>valueUnbound()</code> on the object.
 861  
      *
 862  
      * @param name Name of the object to remove from this session.
 863  
      *
 864  
      * @exception IllegalStateException if this method is called on an
 865  
      *  invalidated session
 866  
      *
 867  
      * @deprecated As of Version 2.2, this method is replaced by
 868  
      *  <code>removeAttribute()</code>
 869  
      */
 870  
     public void removeValue(String name) {
 871  
 
 872  0
         removeAttribute(name);
 873  
 
 874  0
     }
 875  
 
 876  
 
 877  
     /**
 878  
      * Bind an object to this session, using the specified name.  If an object
 879  
      * of the same name is already bound to this session, the object is
 880  
      * replaced.
 881  
      * <p>
 882  
      * After this method executes, and if the object implements
 883  
      * <code>HttpSessionBindingListener</code>, the container calls
 884  
      * <code>valueBound()</code> on the object.
 885  
      *
 886  
      * @param name Name to which the object is bound, cannot be null
 887  
      * @param value Object to be bound, cannot be null
 888  
      *
 889  
      * @exception IllegalArgumentException if an attempt is made to add a
 890  
      *  non-serializable object in an environment marked distributable.
 891  
      * @exception IllegalStateException if this method is called on an
 892  
      *  invalidated session
 893  
      */
 894  
     public void setAttribute(String name, Object value) {
 895  
 
 896  
         // Name cannot be null
 897  0
         if (name == null) {
 898  0
             throw new IllegalArgumentException( "Attribute name cannot be null" );
 899  
         }
 900  
 
 901  
         // Null value is the same as removeAttribute()
 902  0
         if (value == null) {
 903  0
             removeAttribute(name);
 904  0
             return;
 905  
         }
 906  
 
 907  
         // Validate our current state
 908  0
         if (!isValid) {
 909  0
             throw new IllegalStateException( "You cannot call this method on an invalidated session" );
 910  
         }
 911  0
         if (distributable &&
 912  
             !(value instanceof Serializable)) {
 913  0
             throw new IllegalArgumentException( "Attribute value must be Serializable" );
 914  
         }
 915  
 
 916  
         // Replace or add this attribute
 917  0
         Object unbound = null;
 918  0
         synchronized (attributes) {
 919  0
             unbound = attributes.get(name);
 920  0
             attributes.put(name, value);
 921  0
         }
 922  
 
 923  
         // Call the valueUnbound() method if necessary
 924  0
         if ((unbound != null) &&
 925  
             (unbound instanceof HttpSessionBindingListener)) {
 926  0
             ((HttpSessionBindingListener) unbound).valueUnbound
 927  
               (new HttpSessionBindingEvent((HttpSession) this, name));
 928  
         }
 929  
 
 930  
         // Call the valueBound() method if necessary
 931  0
         HttpSessionBindingEvent event = null;
 932  0
         if (unbound != null)
 933  0
             event = new HttpSessionBindingEvent
 934  
                 ((HttpSession) this, name, unbound);
 935  
         else
 936  0
             event = new HttpSessionBindingEvent
 937  
                 ((HttpSession) this, name, value);
 938  0
         if (value instanceof HttpSessionBindingListener)
 939  0
             ((HttpSessionBindingListener) value).valueBound(event);
 940  
 
 941  
         // Notify interested application event listeners
 942  
 /*        
 943  
         StandardContext context = (StandardContext) manager.getContainer();
 944  
         Object listeners[] = context.getApplicationListeners();
 945  
         if (listeners == null)
 946  
             return;
 947  
         for (int i = 0; i < listeners.length; i++) {
 948  
             if (!(listeners[i] instanceof HttpSessionAttributeListener))
 949  
                 continue;
 950  
             HttpSessionAttributeListener listener =
 951  
                 (HttpSessionAttributeListener) listeners[i];
 952  
             try {
 953  
                 if (unbound != null) {
 954  
                     context.fireContainerEvent("beforeSessionAttributeReplaced",
 955  
                                                listener);
 956  
                     listener.attributeReplaced(event);
 957  
                     context.fireContainerEvent("afterSessionAttributeReplaced",
 958  
                                                listener);
 959  
                 } else {
 960  
                     context.fireContainerEvent("beforeSessionAttributeAdded",
 961  
                                                listener);
 962  
                     listener.attributeAdded(event);
 963  
                     context.fireContainerEvent("afterSessionAttributeAdded",
 964  
                                                listener);
 965  
                 }
 966  
             } catch (Throwable t) {
 967  
                 if (unbound != null)
 968  
                     context.fireContainerEvent("afterSessionAttributeReplaced",
 969  
                                                listener);
 970  
                 else
 971  
                     context.fireContainerEvent("afterSessionAttributeAdded",
 972  
                                                listener);
 973  
                 // FIXME - should we do anything besides log these?
 974  
                 log( "Exception firing attribute event", t);
 975  
             }
 976  
         }
 977  
 */
 978  0
     }
 979  
 
 980  
 
 981  
     // -------------------------------------------- HttpSession Private Methods
 982  
 
 983  
 
 984  
     /**
 985  
      * Read a serialized version of this session object from the specified
 986  
      * object input stream.
 987  
      * <p>
 988  
      * <b>IMPLEMENTATION NOTE</b>:  The reference to the owning Manager
 989  
      * is not restored by this method, and must be set explicitly.
 990  
      *
 991  
      * @param stream The input stream to read from
 992  
      *
 993  
      * @exception ClassNotFoundException if an unknown class is specified
 994  
      * @exception IOException if an input/output error occurs
 995  
      */
 996  
     private void readObject(ObjectInputStream stream)
 997  
         throws ClassNotFoundException, IOException {
 998  
 
 999  
         // Deserialize the scalar instance variables (except Manager)
 1000  0
         authType = null;        // Transient only
 1001  0
         creationTime = ((Long) stream.readObject()).longValue();
 1002  0
         lastAccessedTime = ((Long) stream.readObject()).longValue();
 1003  0
         maxInactiveInterval = ((Integer) stream.readObject()).intValue();
 1004  0
         isNew = ((Boolean) stream.readObject()).booleanValue();
 1005  0
         isValid = ((Boolean) stream.readObject()).booleanValue();
 1006  0
         thisAccessedTime = ((Long) stream.readObject()).longValue();
 1007  0
         principal = null;        // Transient only
 1008  0
         setId((String) stream.readObject());
 1009  0
         if (debug >= 2)
 1010  0
             log("readObject() loading session " + id);
 1011  
 
 1012  
         // Deserialize the attribute count and attribute values
 1013  0
         if (attributes == null)
 1014  0
             attributes = new HashMap();
 1015  0
         int n = ((Integer) stream.readObject()).intValue();
 1016  0
         boolean isValidSave = isValid;
 1017  0
         isValid = true;
 1018  0
         for (int i = 0; i < n; i++) {
 1019  0
             String name = (String) stream.readObject();
 1020  0
             Object value = (Object) stream.readObject();
 1021  0
             if ((value instanceof String) && (value.equals(NOT_SERIALIZED)))
 1022  0
                 continue;
 1023  0
             if (debug >= 2)
 1024  0
                 log("  loading attribute '" + name +
 1025  
                     "' with value '" + value + "'");
 1026  0
             synchronized (attributes) {
 1027  0
                 attributes.put(name, value);
 1028  0
             }
 1029  
         }
 1030  0
         isValid = isValidSave;
 1031  
 
 1032  0
     }
 1033  
 
 1034  
 
 1035  
     /**
 1036  
      * Write a serialized version of this session object to the specified
 1037  
      * object output stream.
 1038  
      * <p>
 1039  
      * <b>IMPLEMENTATION NOTE</b>:  The owning Manager will not be stored
 1040  
      * in the serialized representation of this Session.  After calling
 1041  
      * <code>readObject()</code>, you must set the associated Manager
 1042  
      * explicitly.
 1043  
      * <p>
 1044  
      * <b>IMPLEMENTATION NOTE</b>:  Any attribute that is not Serializable
 1045  
      * will be unbound from the session, with appropriate actions if it
 1046  
      * implements HttpSessionBindingListener.  If you do not want any such
 1047  
      * attributes, be sure the <code>distributable</code> property of the
 1048  
      * associated Manager is set to <code>true</code>.
 1049  
      *
 1050  
      * @param stream The output stream to write to
 1051  
      *
 1052  
      * @exception IOException if an input/output error occurs
 1053  
      */
 1054  
     private void writeObject(ObjectOutputStream stream) throws IOException {
 1055  
 
 1056  
         // Write the scalar instance variables (except Manager)
 1057  0
         stream.writeObject(new Long(creationTime));
 1058  0
         stream.writeObject(new Long(lastAccessedTime));
 1059  0
         stream.writeObject(new Integer(maxInactiveInterval));
 1060  0
         stream.writeObject(new Boolean(isNew));
 1061  0
         stream.writeObject(new Boolean(isValid));
 1062  0
         stream.writeObject(new Long(thisAccessedTime));
 1063  0
         stream.writeObject(id);
 1064  0
         if (debug >= 2)
 1065  0
             log("writeObject() storing session " + id);
 1066  
 
 1067  
         // Accumulate the names of serializable and non-serializable attributes
 1068  0
         String keys[] = keys();
 1069  0
         ArrayList saveNames = new ArrayList();
 1070  0
         ArrayList saveValues = new ArrayList();
 1071  0
         ArrayList unbinds = new ArrayList();
 1072  0
         for (int i = 0; i < keys.length; i++) {
 1073  0
             Object value = null;
 1074  0
             synchronized (attributes) {
 1075  0
                 value = attributes.get(keys[i]);
 1076  0
             }
 1077  0
             if (value == null)
 1078  0
                 continue;
 1079  0
             else if (value instanceof Serializable) {
 1080  0
                 saveNames.add(keys[i]);
 1081  0
                 saveValues.add(value);
 1082  
             } else
 1083  0
                 unbinds.add(keys[i]);
 1084  
         }
 1085  
 
 1086  
         // Serialize the attribute count and the Serializable attributes
 1087  0
         int n = saveNames.size();
 1088  0
         stream.writeObject(new Integer(n));
 1089  0
         for (int i = 0; i < n; i++) {
 1090  0
             stream.writeObject((String) saveNames.get(i));
 1091  
             try {
 1092  0
                 stream.writeObject(saveValues.get(i));
 1093  0
                 if (debug >= 2)
 1094  0
                     log("  storing attribute '" + saveNames.get(i) +
 1095  
                         "' with value '" + saveValues.get(i) + "'");
 1096  0
             } catch (NotSerializableException e) {
 1097  0
                 log( "Session is not serializable for attribute name: " + saveNames.get(i) + " and session id: " + id, e);
 1098  0
                 stream.writeObject(NOT_SERIALIZED);
 1099  0
                 if (debug >= 2)
 1100  0
                     log("  storing attribute '" + saveNames.get(i) +
 1101  
                         "' with value NOT_SERIALIZED");
 1102  0
                 unbinds.add(saveNames.get(i));
 1103  0
             }
 1104  
         }
 1105  
 
 1106  
         // Unbind the non-Serializable attributes
 1107  0
         Iterator names = unbinds.iterator();
 1108  0
         while (names.hasNext()) {
 1109  0
             removeAttribute((String) names.next());
 1110  
         }
 1111  
 
 1112  0
     }
 1113  
 
 1114  
 
 1115  
     // -------------------------------------------------------- Private Methods
 1116  
 
 1117  
 
 1118  
     /**
 1119  
      * Notify all session event listeners that a particular event has
 1120  
      * occurred for this Session.  The default implementation performs
 1121  
      * this notification synchronously using the calling thread.
 1122  
      *
 1123  
      * @param type Event type
 1124  
      * @param data Event data
 1125  
      */
 1126  
     public void fireSessionEvent(String type, Object data) {
 1127  
 /*    
 1128  
         if (listeners.size() < 1)
 1129  
             return;
 1130  
         SessionEvent event = new SessionEvent(this, type, data);
 1131  
         SessionListener list[] = new SessionListener[0];
 1132  
         synchronized (listeners) {
 1133  
             list = (SessionListener[]) listeners.toArray(list);
 1134  
         }
 1135  
         for (int i = 0; i < list.length; i++)
 1136  
             ((SessionListener) list[i]).sessionEvent(event);
 1137  
 */
 1138  0
     }
 1139  
 
 1140  
 
 1141  
     /**
 1142  
      * Return the names of all currently defined session attributes
 1143  
      * as an array of Strings.  If there are no defined attributes, a
 1144  
      * zero-length array is returned.
 1145  
      */
 1146  
     private String[] keys() {
 1147  
 
 1148  0
         String results[] = new String[0];
 1149  0
         synchronized (attributes) {
 1150  0
             return ((String[]) attributes.keySet().toArray(results));
 1151  0
         }
 1152  
 
 1153  
     }
 1154  
 
 1155  
 
 1156  
     /**
 1157  
      * Log a message to the current ServletContext
 1158  
      *
 1159  
      * @param message Message to be logged
 1160  
      */
 1161  
     protected void log(String message) {
 1162  
 
 1163  0
         servletContext.log(message);
 1164  
 
 1165  0
     }
 1166  
 
 1167  
 
 1168  
     /**
 1169  
      * Log a message to the current ServletContext
 1170  
      *
 1171  
      * @param message Message to be logged
 1172  
      * @param throwable Associated exception
 1173  
      */
 1174  
     protected void log(String message, Throwable throwable) {
 1175  
 
 1176  0
         servletContext.log(message, throwable);
 1177  
 
 1178  0
     }
 1179  
 
 1180  
 
 1181  
 }