001/* 002 * JGrapes Event Driven Framework 003 * Copyright (C) 2017-2018 Michael N. Lipp 004 * 005 * This program is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU Affero General Public License as published by 007 * the Free Software Foundation; either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, but 011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 013 * for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License along 016 * with this program; if not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jgrapes.http; 020 021import java.io.Serializable; 022import java.time.Instant; 023import java.util.Locale; 024import java.util.Map; 025import java.util.Optional; 026import java.util.concurrent.ConcurrentHashMap; 027import org.jgrapes.core.Associator; 028import org.jgrapes.http.LanguageSelector.Selection; 029import org.jgrapes.http.events.Request; 030 031/** 032 * Represents a browser session. Session objects are used to store 033 * data related to the browser session. Servers with a small number 034 * of clients usually keep session objects and the associated data 035 * in memory. Servers with a large number of clients may choose to 036 * only keep a LRU cache of session objects in memory and swap 037 * out (persist) other session objects. Therefore the keys and 038 * values stored using a session object must be serializable. 039 * 040 * Occasionally, data associated with a session object is already 041 * persisted anyway, because its lifetime is beyond that of a session. 042 * To avoid persisting such data twice, the session provides a special 043 * area for "transient data". If components choose to store data 044 * in this area, they must always check before use if the data is 045 * available and recover it if necessary. Using this approach, the 046 * components automatically profit from the LRU caching mechanism 047 * provided by the session manager. As an alternative, components 048 * can use the session {@link #id()} as key to manage the data 049 * on their own. 050 * 051 * Before removal, the {@link SessionManager} calls the {@link #close} 052 * method. The default implementation iterates through all values 053 * in the transient data map and calls {@link AutoCloseable#close} 054 * for each one that implements the {@link AutoCloseable} interface. 055 * 056 * Implementations should override {@link Object#hashCode()} 057 * and {@link Object#equals(Object)} in such a way 058 * that the session id is the only relevant attribute (cannot be 059 * done by default methods of the interface). 060 * 061 * Note that a browser can issue several requests related to the same 062 * session in parallel. Implementations of this interface must 063 * therefore ensure the thread safety of all methods provided by {@link Map}. 064 * Operations that require consistency across several operations should 065 * synchronize on the session object. 066 */ 067public interface Session extends Map<Serializable, Serializable> { 068 069 /** 070 * Obtains a {@link Session} from an {@link Associator}. The 071 * associator must be a {@link Request} (see 072 * {@link SessionManager#onRequest}) or a web socket channel (see 073 * {@link SessionManager#onProtocolSwitchAccepted}). As the existence of 074 * an association can safely be assumed in handlers handling 075 * {@link Request}s or handlers handling events on web socket channels, 076 * the method returns {@link Session} and not `Optional<Session>`. 077 * 078 * The method should 079 * only be used in contexts where an existing association can be assumed. 080 * 081 * @param associator the associator 082 * @return the session 083 */ 084 static Session from(Associator associator) { 085 return associator.associatedGet(Session.class).get(); 086 } 087 088 /** 089 * Returns the session id. 090 * 091 * @return the id 092 */ 093 @SuppressWarnings("PMD.ShortMethodName") 094 String id(); 095 096 /** 097 * Returns the creation time stamp. 098 * 099 * @return the creation time stamp 100 */ 101 Instant createdAt(); 102 103 /** 104 * Returns the last used (referenced in request) time stamp. 105 * 106 * @return the last used timestamp 107 */ 108 Instant lastUsedAt(); 109 110 /** 111 * Updates the last used time stamp. 112 */ 113 void updateLastUsedAt(); 114 115 /** 116 * Return the storage area for transient data. Usually implemented 117 * by a {@link ConcurrentHashMap}. Other implementations must 118 * at least provide the same support for concurrency as 119 * {@link ConcurrentHashMap}. 120 * 121 * @return the storage area 122 */ 123 Map<Object, Object> transientData(); 124 125 /** 126 * Iterates through all values in the transient data map and 127 * calls {@link AutoCloseable#close} for each that implements 128 * the {@link AutoCloseable} interface. Any exceptions are ignored. 129 */ 130 @SuppressWarnings({ "PMD.AvoidCatchingGenericException", 131 "PMD.EmptyCatchBlock" }) 132 default void close() { 133 for (Object item : transientData().values()) { 134 if (item instanceof AutoCloseable) { 135 try { 136 ((AutoCloseable) item).close(); 137 } catch (Exception e) { 138 // Ignored 139 } 140 } 141 } 142 } 143 144 /** 145 * Convenience method for retrieving the locale 146 * set by {@link LanguageSelector} from the session. 147 * 148 * @return the locale 149 */ 150 default Locale locale() { 151 return Optional.ofNullable((Selection) get(Selection.class)) 152 .map(selection -> selection.get()[0]).orElse(Locale.getDefault()); 153 } 154 155}