SessionAttributesHandler.java 6.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/*
 * Copyright 2002-2011 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
 *
 *      http://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.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.core.annotation.AnnotationUtils;
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;

/**
34 35
 * Manages handler-specific session attributes declared via @{@link SessionAttributes}. 
 * Actual storage is performed through an instance of {@link SessionAttributeStore}.
36
 * 
R
Rossen Stoyanchev 已提交
37 38 39 40 41 42
 * <p>A typical scenario begins with a controller adding attributes to the 
 * {@link org.springframework.ui.Model Model}. At the end of the request, model 
 * attributes are checked to see if any of them match the names and types declared 
 * via @{@link SessionAttributes}. Matching model attributes are "promoted" to 
 * the session and remain there until the controller calls 
 * {@link SessionStatus#setComplete()} to indicate the session attributes are
43
 * no longer needed and can be removed. 
44 45 46 47 48 49 50 51
 * 
 * @author Rossen Stoyanchev
 * @since 3.1
 */
public class SessionAttributesHandler {

	private final Set<String> attributeNames = new HashSet<String>();

52
	private final Set<Class<?>> attributeTypes = new HashSet<Class<?>>();
53 54 55

	private final Set<String> resolvedAttributeNames = Collections.synchronizedSet(new HashSet<String>(4));

56
	private final SessionAttributeStore sessionAttributeStore;
57 58

	/**
59 60 61 62
	 * Creates a {@link SessionAttributesHandler} instance for the specified handler type 
	 * Inspects the given handler type for the presence of an @{@link SessionAttributes} 
	 * and stores that information to identify model attribute that need to be stored, 
	 * retrieved, or removed from the session.
63
	 * @param handlerType the handler type to inspect for a {@link SessionAttributes} annotation
64
	 * @param sessionAttributeStore used for session access
65
	 */
66 67 68
	public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
		Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null.");
		this.sessionAttributeStore = sessionAttributeStore;
69 70 71 72
		
		SessionAttributes annotation = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class);
		if (annotation != null) {
			this.attributeNames.addAll(Arrays.asList(annotation.value())); 
73
			this.attributeTypes.addAll(Arrays.<Class<?>>asList(annotation.types()));
74 75 76 77
		}		
	}

	/**
78 79
	 * Whether the controller represented by this handler has declared session 
	 * attribute names or types of interest via @{@link SessionAttributes}. 
80 81 82 83 84 85
	 */
	public boolean hasSessionAttributes() {
		return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0)); 
	}
	
	/**
86 87 88 89 90 91 92 93
	 * Whether the controller represented by this instance has declared a specific 
	 * attribute as a session attribute via @{@link SessionAttributes}. 
	 * 
	 * <p>Attributes successfully resolved through this method are "remembered" and
	 * used by calls to {@link #retrieveAttributes(WebRequest)} and 
	 * {@link #cleanupAttributes(WebRequest)}. In other words unless attributes 
	 * have been resolved and stored before, retrieval and cleanup have no impact.
	 * 
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
	 * @param attributeName the attribute name to check, must not be null
	 * @param attributeType the type for the attribute, not required but should be provided when
	 * available as session attributes of interest can be matched by type
	 */
	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.resolvedAttributeNames.add(attributeName);
			return true;
		}
		else {
			return false;
		}
	}

	/**
110 111 112 113
	 * Stores a subset of the given attributes in the session. Attributes not 
	 * declared as session attributes via @{@link SessionAttributes} are ignored. 
	 * @param request the current request
	 * @param attributes candidate attributes for session storage
114
	 */
115 116 117 118 119 120 121 122 123
	public void storeAttributes(WebRequest request, Map<String, ?> attributes) {
		for (String name : attributes.keySet()) {
			Object value = attributes.get(name);
			Class<?> attrType = (value != null) ? value.getClass() : null;
			
			if (isHandlerSessionAttribute(name, attrType)) {
				this.sessionAttributeStore.storeAttribute(request, name, value);
			}
		}
124 125 126
	}
	
	/**
127 128
	 * Retrieves "remembered" (i.e. previously stored) session attributes 
	 * for the controller represented by this handler.
129
	 * @param request the current request
130
	 * @return a map with handler session attributes; possibly empty.
131
	 */
132
	public Map<String, Object> retrieveAttributes(WebRequest request) {
133 134
		Map<String, Object> attributes = new HashMap<String, Object>();
		for (String name : this.resolvedAttributeNames) {
135
			Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
136 137 138 139 140 141 142 143
			if (value != null) {
				attributes.put(name, value);
			}
		}
		return attributes;
	}

	/**
144 145
	 * Cleans "remembered" (i.e. previously stored) session attributes  
	 * for the controller represented by this handler.
146 147
	 * @param request the current request
	 */
148
	public void cleanupAttributes(WebRequest request) {
149
		for (String attributeName : this.resolvedAttributeNames) {
150
			this.sessionAttributeStore.cleanupAttribute(request, attributeName);
151 152 153 154
		}
	}

	/**
155
	 * A pass-through call to the underlying {@link SessionAttributeStore}.
156
	 * @param request the current request
157 158
	 * @param attributeName the name of the attribute of interest
	 * @return the attribute value or {@code null}
159
	 */
160 161
	Object retrieveAttribute(WebRequest request, String attributeName) {
		return this.sessionAttributeStore.retrieveAttribute(request, attributeName);
162
	}
163
	
164
}