From bad70bb0ca40a8a4e9b4ee21a3648cceb08f5e9e Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Thu, 3 Jul 2014 12:07:52 -0500 Subject: [PATCH] Javadoc --- .../springframework/session/MapSession.java | 1 + .../session/MapSessionRepository.java | 2 +- .../org/springframework/session/Session.java | 2 +- .../session/SessionRepository.java | 2 +- .../RedisOperationsSessionRepository.java | 148 +++++- .../web/CookieHttpSessionStrategy.java | 8 +- .../web/HeaderHttpSessionStrategy.java | 1 + .../session/web/HttpSessionStrategy.java | 1 + .../web/OnCommittedResponseWrapper.java | 1 + .../session/web/OncePerRequestFilter.java | 1 + .../session/web/SessionRepositoryFilter.java | 468 +++++++++--------- 11 files changed, 390 insertions(+), 245 deletions(-) diff --git a/spring-session/src/main/java/org/springframework/session/MapSession.java b/spring-session/src/main/java/org/springframework/session/MapSession.java index 34d899c5..9fd3c496 100644 --- a/spring-session/src/main/java/org/springframework/session/MapSession.java +++ b/spring-session/src/main/java/org/springframework/session/MapSession.java @@ -37,6 +37,7 @@ import java.util.UUID; * This implementation has no synchronization, so it is best to use the copy constructor when working on multiple threads. *

* + * @since 1.0 * @author Rob Winch */ public final class MapSession implements Session { diff --git a/spring-session/src/main/java/org/springframework/session/MapSessionRepository.java b/spring-session/src/main/java/org/springframework/session/MapSessionRepository.java index f8705aa2..a0a28b7a 100644 --- a/spring-session/src/main/java/org/springframework/session/MapSessionRepository.java +++ b/spring-session/src/main/java/org/springframework/session/MapSessionRepository.java @@ -26,7 +26,7 @@ import java.util.concurrent.ConcurrentHashMap; * distributed maps provided by NoSQL stores like Redis and Hazelcast. * * @author Rob Winch - * @since 4.0 + * @since 1.0 */ public class MapSessionRepository implements SessionRepository { private final Map sessions; diff --git a/spring-session/src/main/java/org/springframework/session/Session.java b/spring-session/src/main/java/org/springframework/session/Session.java index 9c661e34..08e3564d 100644 --- a/spring-session/src/main/java/org/springframework/session/Session.java +++ b/spring-session/src/main/java/org/springframework/session/Session.java @@ -23,7 +23,7 @@ import java.util.Set; * Session, or even non web related sessions. * * @author Rob Winch - * @since 4.0 + * @since 1.0 */ public interface Session extends Serializable { /** diff --git a/spring-session/src/main/java/org/springframework/session/SessionRepository.java b/spring-session/src/main/java/org/springframework/session/SessionRepository.java index 04d4e210..28bb97c1 100644 --- a/spring-session/src/main/java/org/springframework/session/SessionRepository.java +++ b/spring-session/src/main/java/org/springframework/session/SessionRepository.java @@ -19,7 +19,7 @@ package org.springframework.session; * A repository interface for managing {@link Session} instances. * * @author Rob Winch - * @since 4.0 + * @since 1.0 */ public interface SessionRepository { /** diff --git a/spring-session/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java b/spring-session/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java index b50d0439..e949cb41 100644 --- a/spring-session/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java +++ b/spring-session/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java @@ -20,6 +20,7 @@ import org.springframework.data.redis.core.RedisOperations; import org.springframework.session.MapSession; import org.springframework.session.Session; import org.springframework.session.SessionRepository; +import org.springframework.util.Assert; import java.util.HashMap; import java.util.Map; @@ -27,35 +28,120 @@ import java.util.Set; import java.util.concurrent.TimeUnit; /** - * A {@link org.springframework.session.SessionRepository} that is implemented using Spring Data's {@link org.springframework.data.redis.core.RedisOperations}. In a web environment, this is typically used in combination with - * {@link org.springframework.session.web.SessionRepositoryFilter}. + *

+ * A {@link org.springframework.session.SessionRepository} that is implemented using Spring Data's + * {@link org.springframework.data.redis.core.RedisOperations}. In a web environment, this is typically used in + * combination with {@link org.springframework.session.web.SessionRepositoryFilter}. + *

* + *

Creating a new instance

+ * + * A typical example of how to create a new instance can be seen below: + * + *
+ *  JedisConnectionFactory factory = new JedisConnectionFactory();
+ *
+ *  RedisTemplate template = new RedisTemplate();
+ *  template.setKeySerializer(new StringRedisSerializer());
+ *  template.setHashKeySerializer(new StringRedisSerializer());
+ *  template.setConnectionFactory(factory);
+ *
+ *  RedisOperationsSessionRepository redisSessionRepository = new RedisOperationsSessionRepository(template);
+ * 
+ * + *

+ * For additional information on how to create a RedisTemplate, refer to the + * Spring Data Redis Reference. + *

+ * + *

Storage Details

+ * + *

+ * Each session is stored in Redis as a Hash. Each session is + * set and updated using the HMSET command. An example of how each session + * is stored can be seen below. + *

+ * + *
+ *     HMSET spring-security-sessions: creationTime 1404360000000 maxInactiveInterval 1800 lastAccessedTime 1404360000000 sessionAttr: someAttrValue sessionAttr2: someAttrValue2
+ * 
+ * + *

+ * An expiration is associated to each session using the EXPIRE command based upon the + * {@link org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession#getMaxInactiveInterval()}. + * For example: + *

+ * + *
+ *    EXPIRE spring-security-sessions: 1800
+ * 
+ * + *

+ * The {@link RedisSession} keeps track of the properties that have changed and only updates those. This means if an attribute + * is written once and read many times we only need to write that attribute once. For example, assume the session attribute + * "sessionAttr2" from earlier was updated. The following would be executed upon saving: + *

+ * + *
+ *     HMSET spring-security-sessions: sessionAttr2: newValue
+ *     EXPIRE spring-security-sessions: 1800
+ * 
* * @since 1.0 * * @author Rob Winch */ public class RedisOperationsSessionRepository implements SessionRepository { + /** + * The prefix for each key of the Redis Hash representing a single session. The suffix is the unique session id. + */ private final String BOUNDED_HASH_KEY_PREFIX = "spring-security-sessions:"; + + /** + * The key in the Hash representing {@link org.springframework.session.Session#getCreationTime()} + */ private final String CREATION_TIME_ATTR = "creationTime"; + + /** + * The key in the Hash representing {@link org.springframework.session.Session#getMaxInactiveInterval()} + */ private final String MAX_INACTIVE_ATTR = "maxInactiveInterval"; + + /** + * The key in the Hash representing {@link org.springframework.session.Session#getLastAccessedTime()} + */ private final String LAST_ACCESSED_ATTR = "lastAccessedTime"; + + /** + * The prefix of the key for used for session attributes. The suffix is the name of the session attribute. For + * example, if the session contained an attribute named attributeName, then there would be an entry in the hash named + * sessionAttr:attributeName that mapped to its value. + */ private final String SESSION_ATTR_PREFIX = "sessionAttr:"; + private final RedisOperations redisOperations; - private final RedisOperations redisTemplate; - + /** + * If non-null, this value is used to override {@link RedisSession#setDefaultMaxInactiveInterval(int)}. + */ private Integer defaultMaxInactiveInterval; - public RedisOperationsSessionRepository(RedisOperations redisTemplate) { - this.redisTemplate = redisTemplate; + /** + * Creates a new instance. For an example, refer to the class level javadoc. + * + * @param redisOperations The {@link RedisOperations} to use. Cannot be null. + */ + public RedisOperationsSessionRepository(RedisOperations redisOperations) { + Assert.notNull(redisOperations, "RedisOperations cannot be null"); + this.redisOperations = redisOperations; } /** * Sets the maximum inactive interval in seconds between requests before newly created sessions will be - * invalidated. A negative time indicates that the session will never timeout. + * invalidated. A negative time indicates that the session will never timeout. The default is 1800 (30 minutes). * - * @param defaultMaxInactiveInterval the number of seconds that the {@link Session} should be kept alive between client requests. + * @param defaultMaxInactiveInterval the number of seconds that the {@link Session} should be kept alive between + * client requests. */ public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) { this.defaultMaxInactiveInterval = defaultMaxInactiveInterval; @@ -68,7 +154,7 @@ public class RedisOperationsSessionRepository implements SessionRepository entries = getOperations(id).entries(); + Map entries = getSessionBoundHashOperations(id).entries(); if(entries.isEmpty()) { return null; } @@ -92,7 +178,7 @@ public class RedisOperationsSessionRepository implements SessionRepository getOperations(String sessionId) { + /** + * Gets the {@link BoundHashOperations} to operate on a {@link Session} + * @param sessionId the id of the {@link Session} to work with + * @return the {@link BoundHashOperations} to operate on a {@link Session} + */ + private BoundHashOperations getSessionBoundHashOperations(String sessionId) { String key = getKey(sessionId); - return this.redisTemplate.boundHashOps(key); + return this.redisOperations.boundHashOps(key); } + /** + * A custom implementation of {@link Session} that uses a {@link MapSession} as the basis for its mapping. It keeps + * track of any attributes that have changed. When + * {@link org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession#saveDelta()} is invoked + * all the attributes that have been changed will be persisted. + * + * @since 1.0 + * @author Rob Winch + */ class RedisSession implements Session { private final MapSession cached; private Map delta = new HashMap(); + /** + * Creates a new instance ensuring to mark all of the new attributes to be persisted in the next save operation. + */ private RedisSession() { this(new MapSession()); delta.put(CREATION_TIME_ATTR, getCreationTime()); @@ -124,7 +233,13 @@ public class RedisOperationsSessionRepository implements SessionRepository * HTTP/1.1 200 OK - * Set-Cookie: SESSION=f81d4fae-7dec-11d0-a765-00a0c91e6bf6; Secure; HttpOnly + * Set-Cookie: SESSION=f81d4fae-7dec-11d0-a765-00a0c91e6bf6; Path=/context-root; Secure; HttpOnly * * * The client should now include the session in each request by specifying the same cookie in their request. For example: @@ -50,6 +51,7 @@ import javax.servlet.http.HttpServletResponse; * Set-Cookie: SESSION=f81d4fae-7dec-11d0-a765-00a0c91e6bf6; Expires=Thur, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly * * + * @since 1.0 * @author Rob Winch */ public final class CookieHttpSessionStrategy implements HttpSessionStrategy { diff --git a/spring-session/src/main/java/org/springframework/session/web/HeaderHttpSessionStrategy.java b/spring-session/src/main/java/org/springframework/session/web/HeaderHttpSessionStrategy.java index 6ed89ae4..81bd91f9 100644 --- a/spring-session/src/main/java/org/springframework/session/web/HeaderHttpSessionStrategy.java +++ b/spring-session/src/main/java/org/springframework/session/web/HeaderHttpSessionStrategy.java @@ -47,6 +47,7 @@ import javax.servlet.http.HttpServletResponse; * x-auth-token: * * + * @since 1.0 * @author Rob Winch */ public class HeaderHttpSessionStrategy implements HttpSessionStrategy { diff --git a/spring-session/src/main/java/org/springframework/session/web/HttpSessionStrategy.java b/spring-session/src/main/java/org/springframework/session/web/HttpSessionStrategy.java index db240a6f..a9e6ccf0 100644 --- a/spring-session/src/main/java/org/springframework/session/web/HttpSessionStrategy.java +++ b/spring-session/src/main/java/org/springframework/session/web/HttpSessionStrategy.java @@ -23,6 +23,7 @@ import javax.servlet.http.HttpServletResponse; /** * A strategy for mapping HTTP request and responses to a {@link Session}. * + * @since 1.0 * @author Rob Winch */ public interface HttpSessionStrategy { diff --git a/spring-session/src/main/java/org/springframework/session/web/OnCommittedResponseWrapper.java b/spring-session/src/main/java/org/springframework/session/web/OnCommittedResponseWrapper.java index 1ed0f3d5..17afcc48 100644 --- a/spring-session/src/main/java/org/springframework/session/web/OnCommittedResponseWrapper.java +++ b/spring-session/src/main/java/org/springframework/session/web/OnCommittedResponseWrapper.java @@ -26,6 +26,7 @@ import java.util.Locale; * Base class for response wrappers which encapsulate the logic for handling an event when the * {@link javax.servlet.http.HttpServletResponse} is committed. * + * @since 1.0 * @author Rob Winch */ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper { diff --git a/spring-session/src/main/java/org/springframework/session/web/OncePerRequestFilter.java b/spring-session/src/main/java/org/springframework/session/web/OncePerRequestFilter.java index 0d068c18..976b450f 100644 --- a/spring-session/src/main/java/org/springframework/session/web/OncePerRequestFilter.java +++ b/spring-session/src/main/java/org/springframework/session/web/OncePerRequestFilter.java @@ -24,6 +24,7 @@ import java.io.IOException; * Allows for easily ensuring that a request is only invoked once per request. This is a simplified version of spring-web's * OncePerRequestFilter and copied to reduce the foot print required to use the session support. * + * @since 1.0 * @author Rob Winch */ abstract class OncePerRequestFilter implements Filter { diff --git a/spring-session/src/main/java/org/springframework/session/web/SessionRepositoryFilter.java b/spring-session/src/main/java/org/springframework/session/web/SessionRepositoryFilter.java index ec458419..57a8be1c 100644 --- a/spring-session/src/main/java/org/springframework/session/web/SessionRepositoryFilter.java +++ b/spring-session/src/main/java/org/springframework/session/web/SessionRepositoryFilter.java @@ -48,272 +48,292 @@ import java.util.Set; *
  • The client is notified that the session id is no longer valid with {@link HttpSessionStrategy#onInvalidateSession(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
  • * * - * session id is looked up using the provided {@link HttpSessionStrategy}. The same strategy is used to convey the - * session id of newly created {@link org.springframework.session.Session}s to the client. - * + * @since 1.0 * @author Rob Winch */ public class SessionRepositoryFilter extends OncePerRequestFilter { - private final SessionRepository sessionRepository; + private final SessionRepository sessionRepository; - private HttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy(); + private HttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy(); - public SessionRepositoryFilter(SessionRepository sessionRepository) { - this.sessionRepository = sessionRepository; - } + /** + * Creates a new instance + * + * @param sessionRepository the SessionRepository to use. Cannot be null. + */ + public SessionRepositoryFilter(SessionRepository sessionRepository) { + Assert.notNull(sessionRepository, "SessionRepository cannot be null"); + this.sessionRepository = sessionRepository; + } - /** - * Sets the {@link HttpSessionStrategy} to be used. The default is a {@link CookieHttpSessionStrategy}. - * - * @param httpSessionStrategy the {@link HttpSessionStrategy} to use. Cannot be null. - */ - public void setHttpSessionStrategy(HttpSessionStrategy httpSessionStrategy) { - Assert.notNull(httpSessionStrategy,"httpSessionIdStrategy cannot be null"); - this.httpSessionStrategy = httpSessionStrategy; - } + /** + * Sets the {@link HttpSessionStrategy} to be used. The default is a {@link CookieHttpSessionStrategy}. + * + * @param httpSessionStrategy the {@link HttpSessionStrategy} to use. Cannot be null. + */ + public void setHttpSessionStrategy(HttpSessionStrategy httpSessionStrategy) { + Assert.notNull(httpSessionStrategy,"httpSessionIdStrategy cannot be null"); + this.httpSessionStrategy = httpSessionStrategy; + } - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response); - SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,response); - try { - filterChain.doFilter(wrappedRequest, wrappedResponse); - } finally { - wrappedRequest.commitSession(); - } - } + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response); + SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,response); + try { + filterChain.doFilter(wrappedRequest, wrappedResponse); + } finally { + wrappedRequest.commitSession(); + } + } - private final class SessionRepositoryResponseWrapper extends OnCommittedResponseWrapper { + /** + * Allows ensuring that the session is saved if the response is committed. + * + * @author Rob Winch + * @since 1.0 + */ + private final class SessionRepositoryResponseWrapper extends OnCommittedResponseWrapper { - private final SessionRepositoryRequestWrapper request; + private final SessionRepositoryRequestWrapper request; - /** - * @param response the response to be wrapped - */ - public SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper request, HttpServletResponse response) { - super(response); - this.request = request; - } + /** + * @param response the response to be wrapped + */ + public SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper request, HttpServletResponse response) { + super(response); + Assert.notNull(request, "SessionRepositoryRequestWrapper cannot be null"); + this.request = request; + } - @Override - protected void onResponseCommitted() { - request.commitSession(); - } - } + @Override + protected void onResponseCommitted() { + request.commitSession(); + } + } - /** - * A {@link javax.servlet.http.HttpServletRequest} that retrieves the {@link javax.servlet.http.HttpSession} using a - * {@link org.springframework.session.SessionRepository}. - * - * @author Rob Winch - * @since 4.0 - */ - private final class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper { - private HttpSessionWrapper currentSession; - private boolean requestedValidSession; - private final HttpServletResponse response; + /** + * A {@link javax.servlet.http.HttpServletRequest} that retrieves the {@link javax.servlet.http.HttpSession} using a + * {@link org.springframework.session.SessionRepository}. + * + * @author Rob Winch + * @since 1.0 + */ + private final class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper { + private HttpSessionWrapper currentSession; + private boolean requestedValidSession; + private final HttpServletResponse response; - private SessionRepositoryRequestWrapper(HttpServletRequest request, HttpServletResponse response) { - super(request); - this.response = response; - } + private SessionRepositoryRequestWrapper(HttpServletRequest request, HttpServletResponse response) { + super(request); + this.response = response; + } - private void commitSession() { - HttpSessionWrapper wrappedSession = currentSession; - if(wrappedSession == null) { - if(isInvalidateClientSession()) { - httpSessionStrategy.onInvalidateSession(this, response); - } - } else { - S session = wrappedSession.session; - sessionRepository.save(session); - httpSessionStrategy.onNewSession(session, this, response); - } - } + /** + * Uses the HttpSessionStrategy to write the session id tot he response and persist the Session. + */ + private void commitSession() { + HttpSessionWrapper wrappedSession = currentSession; + if(wrappedSession == null) { + if(isInvalidateClientSession()) { + httpSessionStrategy.onInvalidateSession(this, response); + } + } else { + S session = wrappedSession.session; + sessionRepository.save(session); + httpSessionStrategy.onNewSession(session, this, response); + } + } - private boolean isInvalidateClientSession() { - return currentSession == null && requestedValidSession; - } + private boolean isInvalidateClientSession() { + return currentSession == null && requestedValidSession; + } - @Override - public HttpSession getSession(boolean create) { - if(currentSession != null) { - return currentSession; - } - String requestedSessionId = getRequestedSessionId(); - if(requestedSessionId != null) { - S session = sessionRepository.getSession(requestedSessionId); - if(session != null) { - this.requestedValidSession = true; - session.setLastAccessedTime(System.currentTimeMillis()); - currentSession = new HttpSessionWrapper(session, getServletContext()); - currentSession.setNew(false); - return currentSession; - } - } - if(!create) { - return null; - } - S session = sessionRepository.createSession(); - currentSession = new HttpSessionWrapper(session, getServletContext()); - return currentSession; - } + @Override + public HttpSession getSession(boolean create) { + if(currentSession != null) { + return currentSession; + } + String requestedSessionId = getRequestedSessionId(); + if(requestedSessionId != null) { + S session = sessionRepository.getSession(requestedSessionId); + if(session != null) { + this.requestedValidSession = true; + session.setLastAccessedTime(System.currentTimeMillis()); + currentSession = new HttpSessionWrapper(session, getServletContext()); + currentSession.setNew(false); + return currentSession; + } + } + if(!create) { + return null; + } + S session = sessionRepository.createSession(); + currentSession = new HttpSessionWrapper(session, getServletContext()); + return currentSession; + } - @Override - public HttpSession getSession() { - return getSession(true); - } + @Override + public HttpSession getSession() { + return getSession(true); + } - @Override - public String getRequestedSessionId() { - return httpSessionStrategy.getRequestedSessionId(this); - } + @Override + public String getRequestedSessionId() { + return httpSessionStrategy.getRequestedSessionId(this); + } - private final class HttpSessionWrapper implements HttpSession { - final S session; - private final ServletContext servletContext; - private boolean invalidated; - private boolean old; + /** + * Allows creating an HttpSession from a Session instance. + * + * @author Rob Winch + * @since 1.0 + */ + private final class HttpSessionWrapper implements HttpSession { + final S session; + private final ServletContext servletContext; + private boolean invalidated; + private boolean old; - public HttpSessionWrapper(S session, ServletContext servletContext) { - this.session = session; - this.servletContext = servletContext; - } + public HttpSessionWrapper(S session, ServletContext servletContext) { + this.session = session; + this.servletContext = servletContext; + } - void updateLastAccessedTime() { - checkState(); - session.setLastAccessedTime(System.currentTimeMillis()); - } + void updateLastAccessedTime() { + checkState(); + session.setLastAccessedTime(System.currentTimeMillis()); + } - @Override - public long getCreationTime() { - checkState(); - return session.getCreationTime(); - } + @Override + public long getCreationTime() { + checkState(); + return session.getCreationTime(); + } - @Override - public String getId() { - return session.getId(); - } + @Override + public String getId() { + return session.getId(); + } - @Override - public long getLastAccessedTime() { - checkState(); - return session.getLastAccessedTime(); - } + @Override + public long getLastAccessedTime() { + checkState(); + return session.getLastAccessedTime(); + } - @Override - public ServletContext getServletContext() { - return servletContext; - } + @Override + public ServletContext getServletContext() { + return servletContext; + } - @Override - public void setMaxInactiveInterval(int interval) { - session.setMaxInactiveInterval(interval); - } + @Override + public void setMaxInactiveInterval(int interval) { + session.setMaxInactiveInterval(interval); + } - @Override - public int getMaxInactiveInterval() { - return session.getMaxInactiveInterval(); - } + @Override + public int getMaxInactiveInterval() { + return session.getMaxInactiveInterval(); + } - @Override - public HttpSessionContext getSessionContext() { - return NOOP_SESSION_CONTEXT; - } + @Override + public HttpSessionContext getSessionContext() { + return NOOP_SESSION_CONTEXT; + } - @Override - public Object getAttribute(String name) { - checkState(); - return session.getAttribute(name); - } + @Override + public Object getAttribute(String name) { + checkState(); + return session.getAttribute(name); + } - @Override - public Object getValue(String name) { - return getAttribute(name); - } + @Override + public Object getValue(String name) { + return getAttribute(name); + } - @Override - public Enumeration getAttributeNames() { - checkState(); - return Collections.enumeration(session.getAttributeNames()); - } + @Override + public Enumeration getAttributeNames() { + checkState(); + return Collections.enumeration(session.getAttributeNames()); + } - @Override - public String[] getValueNames() { - checkState(); - Set attrs = session.getAttributeNames(); - return attrs.toArray(new String[0]); - } + @Override + public String[] getValueNames() { + checkState(); + Set attrs = session.getAttributeNames(); + return attrs.toArray(new String[0]); + } - @Override - public void setAttribute(String name, Object value) { - checkState(); - session.setAttribute(name, value); - } + @Override + public void setAttribute(String name, Object value) { + checkState(); + session.setAttribute(name, value); + } - @Override - public void putValue(String name, Object value) { - setAttribute(name, value); - } + @Override + public void putValue(String name, Object value) { + setAttribute(name, value); + } - @Override - public void removeAttribute(String name) { - checkState(); - session.removeAttribute(name); - } + @Override + public void removeAttribute(String name) { + checkState(); + session.removeAttribute(name); + } - @Override - public void removeValue(String name) { - removeAttribute(name); - } + @Override + public void removeValue(String name) { + removeAttribute(name); + } - @Override - public final void invalidate() { - checkState(); - this.invalidated = true; - currentSession = null; - sessionRepository.delete(getId()); - } + @Override + public final void invalidate() { + checkState(); + this.invalidated = true; + currentSession = null; + sessionRepository.delete(getId()); + } - public void setNew(boolean isNew) { - this.old = !isNew; - } + public void setNew(boolean isNew) { + this.old = !isNew; + } - @Override - public boolean isNew() { - checkState(); - return !old; - } + @Override + public boolean isNew() { + checkState(); + return !old; + } - private void checkState() { - if(invalidated) { - throw new IllegalStateException("The HttpSession has already be invalidated."); - } - } - } - } + private void checkState() { + if(invalidated) { + throw new IllegalStateException("The HttpSession has already be invalidated."); + } + } + } + } - private static final HttpSessionContext NOOP_SESSION_CONTEXT = new HttpSessionContext() { - @Override - public HttpSession getSession(String sessionId) { - return null; - } + private static final HttpSessionContext NOOP_SESSION_CONTEXT = new HttpSessionContext() { + @Override + public HttpSession getSession(String sessionId) { + return null; + } - @Override - public Enumeration getIds() { - return EMPTY_ENUMERATION; - } - }; + @Override + public Enumeration getIds() { + return EMPTY_ENUMERATION; + } + }; - private final static Enumeration EMPTY_ENUMERATION = new Enumeration() { - @Override - public boolean hasMoreElements() { - return false; - } + private final static Enumeration EMPTY_ENUMERATION = new Enumeration() { + @Override + public boolean hasMoreElements() { + return false; + } - @Override - public String nextElement() { - throw new NoSuchElementException("a"); - } - }; + @Override + public String nextElement() { + throw new NoSuchElementException("a"); + } + }; }