/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.method.annotation;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionAttributeStore;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.context.request.WebRequest;
Manages controller-specific session attributes declared via @SessionAttributes
. Actual storage is delegated to a SessionAttributeStore
instance. When a controller annotated with @SessionAttributes
adds attributes to its model, those attributes are checked against names and types specified via @SessionAttributes
. Matching model attributes are saved in the HTTP session and remain there until the controller calls SessionStatus.setComplete()
.
Author: Rossen Stoyanchev, Juergen Hoeller Since: 3.1
/**
* Manages controller-specific session attributes declared via
* {@link SessionAttributes @SessionAttributes}. Actual storage is
* delegated to a {@link SessionAttributeStore} instance.
*
* <p>When a controller annotated with {@code @SessionAttributes} adds
* attributes to its model, those attributes are checked against names and
* types specified via {@code @SessionAttributes}. Matching model attributes
* are saved in the HTTP session and remain there until the controller calls
* {@link SessionStatus#setComplete()}.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.1
*/
public class SessionAttributesHandler {
private final Set<String> attributeNames = new HashSet<>();
private final Set<Class<?>> attributeTypes = new HashSet<>();
private final Set<String> knownAttributeNames = Collections.newSetFromMap(new ConcurrentHashMap<>(4));
private final SessionAttributeStore sessionAttributeStore;
Create a new session attributes handler. Session attribute names and types are extracted from the @SessionAttributes
annotation, if present, on the given type. Params: - handlerType – the controller type
- sessionAttributeStore – used for session access
/**
* Create a new session attributes handler. Session attribute names and types
* are extracted from the {@code @SessionAttributes} annotation, if present,
* on the given type.
* @param handlerType the controller type
* @param sessionAttributeStore used for session access
*/
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
this.sessionAttributeStore = sessionAttributeStore;
SessionAttributes ann = AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
if (ann != null) {
Collections.addAll(this.attributeNames, ann.names());
Collections.addAll(this.attributeTypes, ann.types());
}
this.knownAttributeNames.addAll(this.attributeNames);
}
Whether the controller represented by this instance has declared any session attributes through an SessionAttributes
annotation. /**
* Whether the controller represented by this instance has declared any
* session attributes through an {@link SessionAttributes} annotation.
*/
public boolean hasSessionAttributes() {
return (!this.attributeNames.isEmpty() || !this.attributeTypes.isEmpty());
}
Whether the attribute name or type match the names and types specified via @SessionAttributes
on the underlying controller. Attributes successfully resolved through this method are "remembered" and subsequently used in retrieveAttributes(WebRequest)
and cleanupAttributes(WebRequest)
.
Params: - attributeName – the attribute name to check
- attributeType – the type for the attribute
/**
* Whether the attribute name or type match the names and types specified
* via {@code @SessionAttributes} on the underlying controller.
* <p>Attributes successfully resolved through this method are "remembered"
* and subsequently used in {@link #retrieveAttributes(WebRequest)} and
* {@link #cleanupAttributes(WebRequest)}.
* @param attributeName the attribute name to check
* @param attributeType the type for the attribute
*/
public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
Assert.notNull(attributeName, "Attribute name must not be null");
if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) {
this.knownAttributeNames.add(attributeName);
return true;
}
else {
return false;
}
}
Store a subset of the given attributes in the session. Attributes not declared as session attributes via @SessionAttributes
are ignored. Params: - request – the current request
- attributes – candidate attributes for session storage
/**
* Store a subset of the given attributes in the session. Attributes not
* declared as session attributes via {@code @SessionAttributes} are ignored.
* @param request the current request
* @param attributes candidate attributes for session storage
*/
public void storeAttributes(WebRequest request, Map<String, ?> attributes) {
attributes.forEach((name, value) -> {
if (value != null && isHandlerSessionAttribute(name, value.getClass())) {
this.sessionAttributeStore.storeAttribute(request, name, value);
}
});
}
Retrieve "known" attributes from the session, i.e. attributes listed by name in @SessionAttributes
or attributes previously stored in the model that matched by type. Params: - request – the current request
Returns: a map with handler session attributes, possibly empty
/**
* Retrieve "known" attributes from the session, i.e. attributes listed
* by name in {@code @SessionAttributes} or attributes previously stored
* in the model that matched by type.
* @param request the current request
* @return a map with handler session attributes, possibly empty
*/
public Map<String, Object> retrieveAttributes(WebRequest request) {
Map<String, Object> attributes = new HashMap<>();
for (String name : this.knownAttributeNames) {
Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
if (value != null) {
attributes.put(name, value);
}
}
return attributes;
}
Remove "known" attributes from the session, i.e. attributes listed by name in @SessionAttributes
or attributes previously stored in the model that matched by type. Params: - request – the current request
/**
* Remove "known" attributes from the session, i.e. attributes listed
* by name in {@code @SessionAttributes} or attributes previously stored
* in the model that matched by type.
* @param request the current request
*/
public void cleanupAttributes(WebRequest request) {
for (String attributeName : this.knownAttributeNames) {
this.sessionAttributeStore.cleanupAttribute(request, attributeName);
}
}
A pass-through call to the underlying SessionAttributeStore
. Params: - request – the current request
- attributeName – the name of the attribute of interest
Returns: the attribute value, or null
if none
/**
* A pass-through call to the underlying {@link SessionAttributeStore}.
* @param request the current request
* @param attributeName the name of the attribute of interest
* @return the attribute value, or {@code null} if none
*/
@Nullable
Object retrieveAttribute(WebRequest request, String attributeName) {
return this.sessionAttributeStore.retrieveAttribute(request, attributeName);
}
}