diff options
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 | 918 |
1 files changed, 429 insertions, 489 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 index e187567..82df58b 100644 --- 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 @@ -36,493 +36,433 @@ 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." ); - } - + * 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."); + } + } |
