summaryrefslogtreecommitdiff
path: root/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WOSession.java
diff options
context:
space:
mode:
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.java528
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." );
+ }
+
+}