diff options
| author | Benjamin Culkin <scorpress@gmail.com> | 2024-05-19 17:56:33 -0400 |
|---|---|---|
| committer | Benjamin Culkin <scorpress@gmail.com> | 2024-05-19 17:56:33 -0400 |
| commit | aedc34d55462a75e329bbf342251ff6504cd117e (patch) | |
| tree | bcc8f1f2352582717b484df302aeea6696b8f000 /projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WOSession.java | |
Initial import from SVN
Diffstat (limited to 'projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WOSession.java')
| -rw-r--r-- | projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WOSession.java | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WOSession.java b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WOSession.java new file mode 100644 index 0000000..e187567 --- /dev/null +++ b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WOSession.java @@ -0,0 +1,528 @@ +/* +Wotonomy: OpenStep design patterns for pure Java applications. +Copyright (C) 2000 Blacksmith, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, see http://www.gnu.org +*/ + +package net.wotonomy.web; + +import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpSession; + +import net.wotonomy.control.EOEditingContext; +import net.wotonomy.control.KeyValueCodingUtilities; +import net.wotonomy.foundation.NSArray; +import net.wotonomy.foundation.NSDate; +import net.wotonomy.foundation.NSDictionary; +import net.wotonomy.foundation.NSKeyValueCodingAdditions; +import net.wotonomy.foundation.NSKeyValueCodingSupport; +import net.wotonomy.foundation.NSMutableArray; +import net.wotonomy.foundation.NSMutableDictionary; + +/** +* A pure java implementation of WOSession. +* +* @author michael@mpowers.net +* @author $Author: cgruber $ +* @version $Revision: 905 $ +*/ +public class WOSession implements Serializable, NSKeyValueCodingAdditions +{ + //NOTE: need to set this when deserialized and on creation + transient private HttpSession session; + + // the current context + transient private WOContext context; + + // the last requested page: an optimization + transient private WOComponent currentPage; + // the last requested page's context id + transient private String currentContextID; + + //FIXME: transient until ec's implement serializable + private transient EOEditingContext defaultEditingContext; + + private NSMutableDictionary state; + private NSMutableDictionary pages; + private NSMutableDictionary permanentPages; + private NSMutableArray stateStack; + private NSMutableArray pageStack; + private NSMutableArray permanentPageStack; + private boolean terminating; + + // used by WOResourceManager to cache dynamic resources + transient NSMutableDictionary dynamicDataCache; + + public static final String WOSessionDidTimeOutNotification + = "WOSessionDidTimeOutNotification"; + public static final String WOSessionDidRestoreNotification + = "WOSessionDidRestoreNotification"; + public static final String WOSessionDidCreateNotification + = "WOSessionDidCreateNotification"; + + /** + * Default constructor. This is called implicitly by + * subclasses in all cases. + */ + public WOSession () + { + session = null; + state = new NSMutableDictionary(); + pages = new NSMutableDictionary(); + permanentPages = new NSMutableDictionary(); + stateStack = NSMutableArray.mutableArrayBackedByList( new LinkedList() ); + pageStack = NSMutableArray.mutableArrayBackedByList( new LinkedList() ); + permanentPageStack = NSMutableArray.mutableArrayBackedByList( new LinkedList() ); + defaultEditingContext = null; + terminating = false; + } + + /** + * Package method to initialize the backing session. + */ + void setServletSession( HttpSession aSession ) + { + session = aSession; + } + + /** + * Package method to set the current context. + */ + void setContext( WOContext aContext ) + { + context = aContext; + } + + /** + * Returns the id of the current session. If no session + * currently exists, return null. + */ + public String sessionID () + { + if ( session != null ) + { + return session.getId(); + } + return null; + } + + /** + * Sets whether distribution is currently enabled. + * This method is not implemented by this implementation + * as the servlet container manages distribution. + */ + public void setDistributionEnabled (boolean enabled) + { + throw new RuntimeException( "Not implemented yet." ); + } + + /** + * Returns whether the session is part of a distributed application. + * This implementation always returns false. + */ + public boolean isDistributionEnabled () + { + return false; + } + + /** + * Sets whether session ids should be stored in cookies. + * This method is not implemented in this implementation + * as the servlet container manages sessions with cookies. + */ + public void setStoresIDsInCookies (boolean cookies) + { + throw new RuntimeException( "Not implemented yet." ); + } + + /** + * Returns whether session ids are currently stored in cookies. + * This implementation always returns true. + */ + public boolean storesIDsInCookies () + { + return true; + } + + /** + * Returns the current expiration date for cookies that store session ids. + */ + public NSDate expirationDateForIDCookies () + { + throw new RuntimeException( "Not implemented yet." ); + } + + /** + * Sets whether session ids should be stored in urls. + * This method is not implemented in this implementation + * as the servlet container manages sessions with cookies. + */ + public void setStoresIDsInURLs (boolean urls) + { + throw new RuntimeException( "Not implemented yet." ); + } + + /** + * Returns whether session ids are currently stored in urls. + * This implementation always returns false. + */ + public boolean storesIDsInURLs () + { + return false; + } + + /** + * Returns the current domain for cookies containing session ids. + */ + public String domainForIDCookies () + { + throw new RuntimeException( "Not implemented yet." ); + } + + /** + * Terminates this session after the completion of the current response. + */ + public void terminate () + { + terminating = true; + session.invalidate(); + } + + /** + * Returns whether the current session will terminate at the completion + * of the current response. + */ + public boolean isTerminating () + { + return terminating; + } + + /** + * Sets the number of seconds after the last request before + * the session should be terminated. + */ + public void setTimeOut (double timeout) + { + session.setMaxInactiveInterval( (int) timeout ); + } + + /** + * Returns the number of seconds after the last request before + * the session should be terminated. + */ + public double timeOut () + { + return session.getMaxInactiveInterval(); + } + + /** + * Sets the languages for which this session has been localized, + * in order of preference. The application will be responsible for + * localizing the content based on the languages found in this array. + */ + public void setLanguages (NSArray anArray) + { + throw new RuntimeException( "Not implemented yet." ); + } + + /** + * Returns the languages for which this session has been localized, + * in order of preference. The application will be responsible for + * localizing the content based on the languages found in this array. + */ + public NSArray languages () + { + throw new RuntimeException( "Not implemented yet." ); + } + + /** + * Stores the specified key-value pair in the session. + */ + public void setObjectForKey (Object anObject, String aKey) + { + state.setObjectForKey( anObject, aKey ); + } + + /** + * Returns the session value associated with the specified key. + */ + public Object objectForKey (String aKey) + { + return state.objectForKey( aKey ); + } + + /** + * Removes the session value associated with the specified key. + */ + public void removeObjectForKey (String aKey) + { + state.removeObjectForKey( aKey ); + } + + /** + * Returns the context for the current request. + */ + public WOContext context () + { + return context; + } + + /** + * Invoked at the beginning of the request-response cycle. + * Override to perform any kind of initialization at the + * start of a request. This implementation does nothing. + */ + public void awake () + { + + } + + /** + * Invoked by the Application to extract user-assigned balues + * and assign them to attributes. This implementation calls + * takeValuesFromRequest on the top-level component. + */ + public void takeValuesFromRequest (WORequest aRequest, WOContext aContext) + { + context().component().takeValuesFromRequest( aRequest, aContext ); + } + + /** + * Invoked by the Application to determine which component is the + * intended recipient of the user's action. This implementation calls + * invokeAction on the top-level component. + */ + public WOActionResults invokeAction (WORequest aRequest, WOContext aContext) + { + return context().component().invokeAction( aRequest, aContext ); + } + + /** + * Invoked by the Application to generate the content of the response. + * This implementation calls appendToResponse on the top-level component. + */ + public void appendToResponse (WOResponse aResponse, WOContext aContext) + { + context().component().appendToResponse( aResponse, aContext ); + } + + /** + * Invoked at the end of the request-response cycle. + * Override to perform any kind of clean-up at the + * end of a request. This implementation does nothing. + */ + public void sleep () + { + + } + + /** + * Returns a list of pages accessed by this session in order + * of their access and named by calling WOComponent.descriptionForResponse. + */ + public NSArray statistics () + { + throw new RuntimeException( "Not implemented yet." ); + } + + /** + * Puts this page in the session's page cache using the current + * context id as the key. + */ + public void savePage (WOComponent aComponent) + { + currentPage = aComponent; + currentContextID = context.contextID(); + + if ( pages.objectForKey( currentContextID ) == null ) + { + byte[] data = KeyValueCodingUtilities.freeze( + aComponent, defaultEditingContext(), aComponent, true ); + System.out.println( "WOSession.savePage: " + currentContextID + " : " + data.length ); + + pages.setObjectForKey( data, currentContextID ); + pageStack.addObject( currentContextID ); + if ( pageStack.count() > context().application().pageCacheSize() ) + { + String id = pageStack.remove( 0 ).toString(); // removeObjectAtIndex + System.out.println( "WOSession.savePage: removing from cache: " + id ); + pages.removeObjectForKey( id ); + } + } + //System.out.println( "savePage: " + this + " : " + id + " : " + pages ); + } + + /** + * Returns the page in the session's page cache corresponding to + * the specified context id. Any special permanent caches are + * searched before the standard page cache. + */ + public WOComponent restorePageForContextID (String anID) + { + if ( anID == null ) return null; + if ( anID.equals( currentContextID ) ) return currentPage; + + WOComponent result = null; + byte[] data = (byte[]) permanentPages.objectForKey( anID ); + if ( data == null ) data = (byte[]) pages.objectForKey( anID ); + if ( data != null ) + { + System.out.println( "WOSession.restorePageForContextID: " + anID + " : " + data.length ); + result = (WOComponent) KeyValueCodingUtilities.thaw( + data, defaultEditingContext(), + WOApplication.application().getClass().getClassLoader(), true ); + } + //System.out.println( "restorePageForContextID: " + this + " : " + anID + " : " + result + " : " + pages ); + return result; + } + + /** + * Puts this page in the special cache is will not get automatically + * flushed like the session page cache. Use this if the page + * is likely to be around for a while, specifically pages within + * frames. + */ + public void savePageInPermanentCache (WOComponent aComponent) + { + currentPage = aComponent; + currentContextID = context.contextID(); + + if ( permanentPages.objectForKey( currentContextID ) == null ) + { + byte[] data = KeyValueCodingUtilities.freeze( + aComponent, defaultEditingContext(), aComponent, true ); + //System.out.println( "WOSession.savePageInPermanentCache: " + // + currentContextID + " : " + data.length ); + + permanentPages.setObjectForKey( data, currentContextID ); + permanentPageStack.addObject( currentContextID ); + if ( permanentPageStack.count() > context().application().pageCacheSize() ) + { + String id = permanentPageStack.remove( 0 ).toString(); // removeObjectAtIndex + permanentPages.removeObjectForKey( id ); + } + } + } + + /** + * Writes a message to the standard error stream. + */ + public static void logString (String aString) + { + System.err.println( aString ); + } + + /** + * Writes a message to the standard error stream + * if debugging is activated. + */ + public static void debugString (String aString) + { + // TODO: Check to see if debugging is enabled. + System.err.println( aString ); + } + + /** + * Returns the default editing context used by this session. + * Defaults to null. + */ + public EOEditingContext defaultEditingContext () + { + return defaultEditingContext; + } + + /** + * Sets the default editing context used by this session. + */ + public void setDefaultEditingContext (EOEditingContext aContext) + { + defaultEditingContext = aContext; + } + + // interface NSKeyValueCodingAdditions + + public Object valueForKeyPath (String aPath) + { + // currently key value coding support also handles keypaths + return valueForKey( aPath ); + } + + public void takeValueForKeyPath (Object aValue, String aPath) + { + // currently key value coding support also handles keypaths + takeValueForKey( aValue, aPath ); + } + + public NSDictionary valuesForKeys (List aKeyList) + { + throw new RuntimeException( "Not implemented yet." ); + } + + public void takeValuesFromDictionary (Map aValueMap) + { + throw new RuntimeException( "Not implemented yet." ); + } + + public Object valueForKey (String aKey) + { // System.out.println( "valueForKey: " + aKey + "->" + this ); + Object result = objectForKey( aKey ); + if ( result == null ) + result = NSKeyValueCodingSupport.valueForKey( this, aKey ); + return result; + } + + public void takeValueForKey (Object aValue, String aKey) + { // System.out.println( "takeValueForKey: " + aKey + " : " + aValue + "->" + this ); + setObjectForKey( aValue, aKey ); + } + + public Object storedValueForKey (String aKey) + { + Object result = objectForKey( aKey ); + if ( result == null ) + NSKeyValueCodingSupport.storedValueForKey( this, aKey ); + return result; + } + + public void takeStoredValueForKey (Object aValue, String aKey) + { + setObjectForKey( aValue, aKey ); + } + + public Object handleQueryWithUnboundKey (String aKey) + { + return NSKeyValueCodingSupport.handleQueryWithUnboundKey( this, aKey ); + } + + public void handleTakeValueForUnboundKey (Object aValue, String aKey) + { + NSKeyValueCodingSupport.handleTakeValueForUnboundKey( this, aValue, aKey ); + } + + public void unableToSetNullForKey (String aKey) + { + NSKeyValueCodingSupport.unableToSetNullForKey( this, aKey ); + } + + public Object validateTakeValueForKeyPath (Object aValue, String aKey) + { + throw new RuntimeException( "Not implemented yet." ); + } + +} |
