Coverage Report - net.wotonomy.web.WOSession
 
Classes in this File Line Coverage Branch Coverage Complexity
WOSession
0% 
0% 
1.532
 
 1  
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2000 Blacksmith, Inc.
 4  
 
 5  
 This library is free software; you can redistribute it and/or
 6  
 modify it under the terms of the GNU Lesser General Public
 7  
 License as published by the Free Software Foundation; either
 8  
 version 2.1 of the License, or (at your option) any later version.
 9  
 
 10  
 This library is distributed in the hope that it will be useful,
 11  
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 Lesser General Public License for more details.
 14  
 
 15  
 You should have received a copy of the GNU Lesser General Public
 16  
 License along with this library; if not, see http://www.gnu.org
 17  
 */
 18  
 
 19  
 package net.wotonomy.web;
 20  
 
 21  
 import java.io.Serializable;
 22  
 import java.util.LinkedList;
 23  
 import java.util.List;
 24  
 import java.util.Map;
 25  
 
 26  
 import javax.servlet.http.HttpSession;
 27  
 
 28  
 import net.wotonomy.control.EOEditingContext;
 29  
 import net.wotonomy.control.KeyValueCodingUtilities;
 30  
 import net.wotonomy.foundation.NSArray;
 31  
 import net.wotonomy.foundation.NSDate;
 32  
 import net.wotonomy.foundation.NSDictionary;
 33  
 import net.wotonomy.foundation.NSKeyValueCodingAdditions;
 34  
 import net.wotonomy.foundation.NSKeyValueCodingSupport;
 35  
 import net.wotonomy.foundation.NSMutableArray;
 36  
 import net.wotonomy.foundation.NSMutableDictionary;
 37  
 
 38  
 /**
 39  
 * A pure java implementation of WOSession.
 40  
 *
 41  
 * @author michael@mpowers.net
 42  
 * @author $Author: cgruber $
 43  
 * @version $Revision: 905 $
 44  
 */
 45  
 public class WOSession implements Serializable, NSKeyValueCodingAdditions
 46  
 {
 47  
     //NOTE: need to set this when deserialized and on creation
 48  
     transient private HttpSession session;
 49  
     
 50  
     // the current context
 51  
     transient private WOContext context;
 52  
     
 53  
     // the last requested page: an optimization
 54  
     transient private WOComponent currentPage;
 55  
     // the last requested page's context id
 56  
     transient private String currentContextID;
 57  
 
 58  
     //FIXME: transient until ec's implement serializable    
 59  
     private transient EOEditingContext defaultEditingContext;
 60  
     
 61  
     private NSMutableDictionary state;
 62  
     private NSMutableDictionary pages;
 63  
     private NSMutableDictionary permanentPages;
 64  
     private NSMutableArray stateStack;
 65  
     private NSMutableArray pageStack;
 66  
     private NSMutableArray permanentPageStack;
 67  
     private boolean terminating;
 68  
     
 69  
     // used by WOResourceManager to cache dynamic resources
 70  
     transient NSMutableDictionary dynamicDataCache;
 71  
     
 72  
     public static final String WOSessionDidTimeOutNotification
 73  
             = "WOSessionDidTimeOutNotification";
 74  
     public static final String WOSessionDidRestoreNotification
 75  
             = "WOSessionDidRestoreNotification";
 76  
     public static final String WOSessionDidCreateNotification
 77  
             = "WOSessionDidCreateNotification";
 78  
         
 79  
         /**
 80  
         * Default constructor.  This is called implicitly by 
 81  
         * subclasses in all cases.
 82  
         */
 83  0
     public WOSession ()
 84  0
     {
 85  0
             session = null;        
 86  0
         state = new NSMutableDictionary();
 87  0
         pages = new NSMutableDictionary();
 88  0
         permanentPages = new NSMutableDictionary();
 89  0
         stateStack = NSMutableArray.mutableArrayBackedByList( new LinkedList() );
 90  0
         pageStack = NSMutableArray.mutableArrayBackedByList( new LinkedList() );
 91  0
         permanentPageStack = NSMutableArray.mutableArrayBackedByList( new LinkedList() );
 92  0
         defaultEditingContext = null;
 93  0
         terminating = false;
 94  0
     }
 95  
     
 96  
         /**
 97  
         * Package method to initialize the backing session.
 98  
         */
 99  
     void setServletSession( HttpSession aSession )
 100  
     {
 101  0
             session = aSession;
 102  0
     }
 103  
 
 104  
     /**
 105  
     * Package method to set the current context.
 106  
     */
 107  
     void setContext( WOContext aContext )
 108  
     {
 109  0
         context = aContext;
 110  0
     }
 111  
     
 112  
         /**
 113  
         * Returns the id of the current session.  If no session
 114  
         * currently exists, return null.
 115  
         */
 116  
     public String sessionID ()
 117  
     {
 118  0
             if ( session != null )
 119  
             {
 120  0
                     return session.getId();
 121  
             }
 122  0
             return null;
 123  
     }
 124  
     
 125  
     /**
 126  
     * Sets whether distribution is currently enabled.
 127  
     * This method is not implemented by this implementation
 128  
     * as the servlet container manages distribution.
 129  
     */
 130  
     public void setDistributionEnabled (boolean enabled)
 131  
     {
 132  0
             throw new RuntimeException( "Not implemented yet." );
 133  
     }
 134  
     
 135  
     /**
 136  
         * Returns whether the session is part of a distributed application.
 137  
         * This implementation always returns false.
 138  
         */
 139  
     public boolean isDistributionEnabled ()
 140  
     {
 141  0
             return false;
 142  
     }
 143  
     
 144  
     /**
 145  
         * Sets whether session ids should be stored in cookies.
 146  
         * This method is not implemented in this implementation
 147  
         * as the servlet container manages sessions with cookies.
 148  
         */
 149  
     public void setStoresIDsInCookies (boolean cookies)
 150  
     {
 151  0
             throw new RuntimeException( "Not implemented yet." );
 152  
     }
 153  
     
 154  
     /**
 155  
         * Returns whether session ids are currently stored in cookies.
 156  
         * This implementation always returns true.
 157  
         */
 158  
     public boolean storesIDsInCookies ()
 159  
     {
 160  0
             return true;
 161  
     }
 162  
     
 163  
     /**
 164  
         * Returns the current expiration date for cookies that store session ids.
 165  
         */
 166  
     public NSDate expirationDateForIDCookies ()
 167  
     {
 168  0
             throw new RuntimeException( "Not implemented yet." );
 169  
     }
 170  
     
 171  
     /**
 172  
         * Sets whether session ids should be stored in urls.
 173  
         * This method is not implemented in this implementation
 174  
         * as the servlet container manages sessions with cookies.
 175  
         */
 176  
     public void setStoresIDsInURLs (boolean urls)
 177  
     {
 178  0
             throw new RuntimeException( "Not implemented yet." );
 179  
     }
 180  
     
 181  
     /**
 182  
         * Returns whether session ids are currently stored in urls.
 183  
         * This implementation always returns false.
 184  
         */
 185  
     public boolean storesIDsInURLs ()
 186  
     {
 187  0
             return false;
 188  
     }
 189  
     
 190  
     /**
 191  
         * Returns the current domain for cookies containing session ids.
 192  
         */
 193  
     public String domainForIDCookies ()
 194  
     {
 195  0
             throw new RuntimeException( "Not implemented yet." );
 196  
     }
 197  
     
 198  
     /**
 199  
         * Terminates this session after the completion of the current response.
 200  
         */
 201  
     public void terminate ()
 202  
     {
 203  0
         terminating = true;
 204  0
             session.invalidate();
 205  0
     }
 206  
     
 207  
     /**
 208  
         * Returns whether the current session will terminate at the completion
 209  
         * of the current response.
 210  
         */
 211  
     public boolean isTerminating ()
 212  
     {
 213  0
             return terminating;
 214  
     }
 215  
     
 216  
     /**
 217  
         * Sets the number of seconds after the last request before 
 218  
         * the session should be terminated.
 219  
         */
 220  
     public void setTimeOut (double timeout)
 221  
     {
 222  0
             session.setMaxInactiveInterval( (int) timeout );
 223  0
     }
 224  
     
 225  
     /**
 226  
         * Returns the number of seconds after the last request before 
 227  
         * the session should be terminated.
 228  
         */
 229  
     public double timeOut ()
 230  
     {
 231  0
             return session.getMaxInactiveInterval();
 232  
     }
 233  
     
 234  
     /**
 235  
         * Sets the languages for which this session has been localized,
 236  
         * in order of preference.  The application will be responsible for
 237  
         * localizing the content based on the languages found in this array.
 238  
         */
 239  
     public void setLanguages (NSArray anArray)
 240  
     {
 241  0
             throw new RuntimeException( "Not implemented yet." );
 242  
     }
 243  
     
 244  
     /**
 245  
         * Returns the languages for which this session has been localized,
 246  
         * in order of preference.  The application will be responsible for
 247  
         * localizing the content based on the languages found in this array.
 248  
         */
 249  
     public NSArray languages ()
 250  
     {
 251  0
             throw new RuntimeException( "Not implemented yet." );
 252  
     }
 253  
     
 254  
     /**
 255  
         * Stores the specified key-value pair in the session.
 256  
         */
 257  
     public void setObjectForKey (Object anObject, String aKey)
 258  
     {
 259  0
         state.setObjectForKey( anObject, aKey );
 260  0
     }
 261  
     
 262  
     /**
 263  
         * Returns the session value associated with the specified key.
 264  
         */
 265  
     public Object objectForKey (String aKey)
 266  
     {
 267  0
         return state.objectForKey( aKey );
 268  
     }
 269  
     
 270  
     /**
 271  
         * Removes the session value associated with the specified key.
 272  
         */
 273  
     public void removeObjectForKey (String aKey)
 274  
     {
 275  0
         state.removeObjectForKey( aKey );
 276  0
     }
 277  
     
 278  
     /**
 279  
         * Returns the context for the current request.
 280  
         */
 281  
     public WOContext context ()
 282  
     {
 283  0
         return context;
 284  
     }
 285  
     
 286  
     /**
 287  
         * Invoked at the beginning of the request-response cycle.
 288  
         * Override to perform any kind of initialization at the 
 289  
         * start of a request.  This implementation does nothing.
 290  
         */
 291  
     public void awake ()
 292  
     {
 293  
     
 294  0
     }
 295  
     
 296  
     /**
 297  
         * Invoked by the Application to extract user-assigned balues
 298  
     * and assign them to attributes.  This implementation calls
 299  
     * takeValuesFromRequest on the top-level component.
 300  
         */
 301  
     public void takeValuesFromRequest (WORequest aRequest, WOContext aContext)
 302  
     {
 303  0
         context().component().takeValuesFromRequest( aRequest, aContext );
 304  0
     }
 305  
     
 306  
     /**
 307  
         * Invoked by the Application to determine which component is the
 308  
     * intended recipient of the user's action.  This implementation calls
 309  
     * invokeAction on the top-level component.
 310  
         */
 311  
     public WOActionResults invokeAction (WORequest aRequest, WOContext aContext)
 312  
     {
 313  0
         return context().component().invokeAction( aRequest, aContext );
 314  
     }
 315  
     
 316  
     /**
 317  
         * Invoked by the Application to generate the content of the response.  
 318  
     * This implementation calls appendToResponse on the top-level component.
 319  
         */
 320  
     public void appendToResponse (WOResponse aResponse, WOContext aContext)
 321  
     {
 322  0
         context().component().appendToResponse( aResponse, aContext );
 323  0
     }
 324  
     
 325  
     /**
 326  
         * Invoked at the end of the request-response cycle.
 327  
         * Override to perform any kind of clean-up at the 
 328  
         * end of a request.  This implementation does nothing.
 329  
         */
 330  
     public void sleep ()
 331  
     {
 332  
 
 333  0
     }
 334  
     
 335  
     /**
 336  
         * Returns a list of pages accessed by this session in order
 337  
         * of their access and named by calling WOComponent.descriptionForResponse.
 338  
         */
 339  
     public NSArray statistics ()
 340  
     {
 341  0
             throw new RuntimeException( "Not implemented yet." );
 342  
     }
 343  
     
 344  
     /**
 345  
         * Puts this page in the session's page cache using the current
 346  
         * context id as the key.
 347  
         */
 348  
     public void savePage (WOComponent aComponent)
 349  
     {
 350  0
         currentPage = aComponent;
 351  0
         currentContextID = context.contextID();
 352  
 
 353  0
         if ( pages.objectForKey( currentContextID ) == null )
 354  
         {
 355  0
             byte[] data = KeyValueCodingUtilities.freeze(
 356  0
                 aComponent, defaultEditingContext(), aComponent, true );
 357  0
             System.out.println( "WOSession.savePage: " + currentContextID + " : " + data.length );                
 358  
                 
 359  0
                 pages.setObjectForKey( data, currentContextID );
 360  0
             pageStack.addObject( currentContextID );
 361  0
             if ( pageStack.count() > context().application().pageCacheSize() )
 362  
             {
 363  0
                 String id = pageStack.remove( 0 ).toString(); // removeObjectAtIndex
 364  0
                 System.out.println( "WOSession.savePage: removing from cache: " + id );
 365  0
                 pages.removeObjectForKey( id );
 366  
             }
 367  
         }
 368  
         //System.out.println( "savePage: " + this + " : " + id + " : " + pages );
 369  0
     }
 370  
     
 371  
     /**
 372  
         * Returns the page in the session's page cache corresponding to
 373  
         * the specified context id.  Any special permanent caches are 
 374  
         * searched before the standard page cache.
 375  
         */
 376  
     public WOComponent restorePageForContextID (String anID)
 377  
     {
 378  0
         if ( anID == null ) return null;
 379  0
         if ( anID.equals( currentContextID ) ) return currentPage;
 380  
         
 381  0
         WOComponent result = null;
 382  0
         byte[] data = (byte[]) permanentPages.objectForKey( anID );
 383  0
         if ( data == null ) data = (byte[]) pages.objectForKey( anID );
 384  0
         if ( data != null ) 
 385  
         { 
 386  0
             System.out.println( "WOSession.restorePageForContextID: " + anID + " : " + data.length );                
 387  0
             result = (WOComponent) KeyValueCodingUtilities.thaw( 
 388  0
                 data, defaultEditingContext(), 
 389  0
                 WOApplication.application().getClass().getClassLoader(), true );
 390  
         }
 391  
         //System.out.println( "restorePageForContextID: " + this + " : " + anID + " : " + result + " : " + pages );
 392  0
         return result;
 393  
     }
 394  
     
 395  
     /**
 396  
         * Puts this page in the special cache is will not get automatically
 397  
         * flushed like the session page cache.  Use this if the page
 398  
         * is likely to be around for a while, specifically pages within
 399  
     * frames.
 400  
         */
 401  
     public void savePageInPermanentCache (WOComponent aComponent)
 402  
     {
 403  0
         currentPage = aComponent;
 404  0
         currentContextID = context.contextID();
 405  
         
 406  0
         if ( permanentPages.objectForKey( currentContextID ) == null )
 407  
         {
 408  0
             byte[] data = KeyValueCodingUtilities.freeze(
 409  0
                 aComponent, defaultEditingContext(), aComponent, true );
 410  
             //System.out.println( "WOSession.savePageInPermanentCache: " 
 411  
             // + currentContextID + " : " + data.length );                
 412  
                 
 413  0
                 permanentPages.setObjectForKey( data, currentContextID );
 414  0
             permanentPageStack.addObject( currentContextID );
 415  0
             if ( permanentPageStack.count() > context().application().pageCacheSize() )
 416  
             {
 417  0
                 String id = permanentPageStack.remove( 0 ).toString(); // removeObjectAtIndex
 418  0
                 permanentPages.removeObjectForKey( id );
 419  
             }
 420  
         }
 421  0
     }
 422  
     
 423  
         /**
 424  
         * Writes a message to the standard error stream.
 425  
         */
 426  
     public static void logString (String aString)
 427  
     {
 428  0
             System.err.println( aString );
 429  0
     }
 430  
 
 431  
         /**
 432  
         * Writes a message to the standard error stream
 433  
         * if debugging is activated.
 434  
         */
 435  
     public static void debugString (String aString)
 436  
     {
 437  
             // TODO: Check to see if debugging is enabled.
 438  0
             System.err.println( aString );
 439  0
     }
 440  
 
 441  
     /**
 442  
     * Returns the default editing context used by this session.
 443  
     * Defaults to null.
 444  
     */    
 445  
     public EOEditingContext defaultEditingContext ()
 446  
     {
 447  0
         return defaultEditingContext;
 448  
     }
 449  
     
 450  
     /**
 451  
     * Sets the default editing context used by this session.
 452  
     */
 453  
     public void setDefaultEditingContext (EOEditingContext aContext)
 454  
     {
 455  0
         defaultEditingContext = aContext;
 456  0
     }
 457  
     
 458  
     // interface NSKeyValueCodingAdditions
 459  
     
 460  
     public Object valueForKeyPath (String aPath)
 461  
     {
 462  
         // currently key value coding support also handles keypaths
 463  0
             return valueForKey( aPath );
 464  
     }
 465  
 
 466  
     public void takeValueForKeyPath (Object aValue, String aPath)
 467  
     {
 468  
         // currently key value coding support also handles keypaths
 469  0
             takeValueForKey( aValue, aPath );
 470  0
     }
 471  
 
 472  
     public NSDictionary valuesForKeys (List aKeyList)
 473  
     {
 474  0
             throw new RuntimeException( "Not implemented yet." );
 475  
     }
 476  
 
 477  
     public void takeValuesFromDictionary (Map aValueMap)
 478  
     {
 479  0
             throw new RuntimeException( "Not implemented yet." );
 480  
     }
 481  
 
 482  
     public Object valueForKey (String aKey)
 483  
     { // System.out.println( "valueForKey: " + aKey + "->" + this );      
 484  0
             Object result = objectForKey( aKey );
 485  0
         if ( result == null ) 
 486  0
             result = NSKeyValueCodingSupport.valueForKey( this, aKey );
 487  0
         return result;
 488  
     }
 489  
 
 490  
     public void takeValueForKey (Object aValue, String aKey)
 491  
     { // System.out.println( "takeValueForKey: " + aKey + " : " + aValue + "->" + this );      
 492  0
         setObjectForKey( aValue, aKey );
 493  0
     }
 494  
 
 495  
     public Object storedValueForKey (String aKey)
 496  
     {
 497  0
             Object result = objectForKey( aKey );
 498  0
         if ( result == null ) 
 499  0
             NSKeyValueCodingSupport.storedValueForKey( this, aKey );
 500  0
         return result;
 501  
     }
 502  
 
 503  
     public void takeStoredValueForKey (Object aValue, String aKey)
 504  
     {
 505  0
         setObjectForKey( aValue, aKey );
 506  0
     }
 507  
 
 508  
     public Object handleQueryWithUnboundKey (String aKey)
 509  
     {
 510  0
             return NSKeyValueCodingSupport.handleQueryWithUnboundKey( this, aKey );
 511  
     }
 512  
 
 513  
     public void handleTakeValueForUnboundKey (Object aValue, String aKey)
 514  
     {
 515  0
             NSKeyValueCodingSupport.handleTakeValueForUnboundKey( this, aValue, aKey );
 516  0
     }
 517  
 
 518  
     public void unableToSetNullForKey (String aKey)
 519  
     {
 520  0
             NSKeyValueCodingSupport.unableToSetNullForKey( this, aKey );
 521  0
     }
 522  
 
 523  
     public Object validateTakeValueForKeyPath (Object aValue, String aKey)
 524  
     {
 525  0
             throw new RuntimeException( "Not implemented yet." );
 526  
     }
 527  
     
 528  
 }