/** * Return the {@link SessionAttributesHandler} instance for the given handler type * (never {@code null}). */ private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) { Class<?> handlerType = handlerMethod.getBeanType(); SessionAttributesHandler sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType); if (sessionAttrHandler == null) { synchronized (this.sessionAttributesHandlerCache) { sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType); if (sessionAttrHandler == null) { sessionAttrHandler = new SessionAttributesHandler(handlerType, this.sessionAttributeStore); this.sessionAttributesHandlerCache.put(handlerType, sessionAttrHandler); } } } return sessionAttrHandler; }
/** * Promote model attributes listed as {@code @SessionAttributes} to the session. * Add {@link BindingResult} attributes where necessary. * @param request the current request * @param container contains the model to update * @throws Exception if creating BindingResult attributes fails */ public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception { ModelMap defaultModel = container.getDefaultModel(); if (container.getSessionStatus().isComplete()){ this.sessionAttributesHandler.cleanupAttributes(request); } else { this.sessionAttributesHandler.storeAttributes(request, defaultModel); } if (!container.isRequestHandled() && container.getModel() == defaultModel) { updateBindingResult(request, defaultModel); } }
/** * Whether the given attribute requires a {@link BindingResult} in the model. */ private boolean isBindingCandidate(String attributeName, Object value) { if (attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) { return false; } if (this.sessionAttributesHandler.isHandlerSessionAttribute(attributeName, value.getClass())) { return true; } return (!value.getClass().isArray() && !(value instanceof Collection) && !(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass())); }
/** * Populate the model in the following order: * <ol> * <li>Retrieve "known" session attributes listed as {@code @SessionAttributes}. * <li>Invoke {@code @ModelAttribute} methods * <li>Find {@code @ModelAttribute} method arguments also listed as * {@code @SessionAttributes} and ensure they're present in the model raising * an exception if necessary. * </ol> * @param request the current request * @param container a container with the model to be initialized * @param handlerMethod the method for which the model is initialized * @throws Exception may arise from {@code @ModelAttribute} methods */ public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod) throws Exception { Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request); container.mergeAttributes(sessionAttributes); invokeModelAttributeMethods(request, container); for (String name : findSessionAttributeArguments(handlerMethod)) { if (!container.containsAttribute(name)) { Object value = this.sessionAttributesHandler.retrieveAttribute(request, name); if (value == null) { throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name); } container.addAttribute(name, value); } } }
@Test public void retrieveAttributes() throws Exception { sessionAttributeStore.storeAttribute(request, "attr1", "value1"); sessionAttributeStore.storeAttribute(request, "attr2", "value2"); sessionAttributeStore.storeAttribute(request, "attr3", new TestBean()); sessionAttributeStore.storeAttribute(request, "attr4", new TestBean()); assertEquals("Named attributes (attr1, attr2) should be 'known' right away", new HashSet<>(asList("attr1", "attr2")), sessionAttributesHandler.retrieveAttributes(request).keySet()); // Resolve 'attr3' by type sessionAttributesHandler.isHandlerSessionAttribute("attr3", TestBean.class); assertEquals("Named attributes (attr1, attr2) and resolved attribute (att3) should be 'known'", new HashSet<>(asList("attr1", "attr2", "attr3")), sessionAttributesHandler.retrieveAttributes(request).keySet()); }
@Test public void cleanupAttributes() throws Exception { sessionAttributeStore.storeAttribute(request, "attr1", "value1"); sessionAttributeStore.storeAttribute(request, "attr2", "value2"); sessionAttributeStore.storeAttribute(request, "attr3", new TestBean()); sessionAttributesHandler.cleanupAttributes(request); assertNull(sessionAttributeStore.retrieveAttribute(request, "attr1")); assertNull(sessionAttributeStore.retrieveAttribute(request, "attr2")); assertNotNull(sessionAttributeStore.retrieveAttribute(request, "attr3")); // Resolve 'attr3' by type sessionAttributesHandler.isHandlerSessionAttribute("attr3", TestBean.class); sessionAttributesHandler.cleanupAttributes(request); assertNull(sessionAttributeStore.retrieveAttribute(request, "attr3")); }
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
@Test public void storeAttributes() throws Exception { ModelMap model = new ModelMap(); model.put("attr1", "value1"); model.put("attr2", "value2"); model.put("attr3", new TestBean()); sessionAttributesHandler.storeAttributes(request, model); assertEquals("value1", sessionAttributeStore.retrieveAttribute(request, "attr1")); assertEquals("value2", sessionAttributeStore.retrieveAttribute(request, "attr2")); assertTrue(sessionAttributeStore.retrieveAttribute(request, "attr3") instanceof TestBean); }
/** * Populate the model in the following order: * <ol> * <li>Retrieve "known" session attributes listed as {@code @SessionAttributes}. * <li>Invoke {@code @ModelAttribute} methods * <li>Find {@code @ModelAttribute} method arguments also listed as * {@code @SessionAttributes} and ensure they're present in the model raising * an exception if necessary. * </ol> * @param request the current request * @param container a container with the model to be initialized * @param handlerMethod the method for which the model is initialized * @throws Exception may arise from {@code @ModelAttribute} methods */ public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod) throws Exception { Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request); container.mergeAttributes(sessionAttributes); invokeModelAttributeMethods(request, container); for (String name : findSessionAttributeArguments(handlerMethod)) { if (!container.containsAttribute(name)) { Object value = this.sessionAttributesHandler.retrieveAttribute(request, name); if (value == null) { throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name); } container.addAttribute(name, value); } } }
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
/** * Promote model attributes listed as {@code @SessionAttributes} to the session. * Add {@link BindingResult} attributes where necessary. * @param request the current request * @param container contains the model to update * @throws Exception if creating BindingResult attributes fails */ public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception { ModelMap defaultModel = container.getDefaultModel(); if (container.getSessionStatus().isComplete()){ this.sessionAttributesHandler.cleanupAttributes(request); } else { this.sessionAttributesHandler.storeAttributes(request, defaultModel); } if (!container.isRequestHandled() && container.getModel() == defaultModel) { updateBindingResult(request, defaultModel); } }
/** * 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); } }); }
/** * Populate the model in the following order: * <ol> * <li>Retrieve "known" session attributes listed as {@code @SessionAttributes}. * <li>Invoke {@code @ModelAttribute} methods * <li>Find {@code @ModelAttribute} method arguments also listed as * {@code @SessionAttributes} and ensure they're present in the model raising * an exception if necessary. * </ol> * @param request the current request * @param container a container with the model to be initialized * @param handlerMethod the method for which the model is initialized * @throws Exception may arise from {@code @ModelAttribute} methods */ public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod) throws Exception { Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request); container.mergeAttributes(sessionAttributes); invokeModelAttributeMethods(request, container); for (String name : findSessionAttributeArguments(handlerMethod)) { if (!container.containsAttribute(name)) { Object value = this.sessionAttributesHandler.retrieveAttribute(request, name); if (value == null) { throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name); } container.addAttribute(name, value); } } }
/** * Return the {@link SessionAttributesHandler} instance for the given handler type * (never {@code null}). */ private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) { Class<?> handlerType = handlerMethod.getBeanType(); SessionAttributesHandler sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType); if (sessionAttrHandler == null) { synchronized (this.sessionAttributesHandlerCache) { sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType); if (sessionAttrHandler == null) { sessionAttrHandler = new SessionAttributesHandler(handlerType, this.sessionAttributeStore); this.sessionAttributesHandlerCache.put(handlerType, sessionAttrHandler); } } } return sessionAttrHandler; }
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
/** * Promote model attributes listed as {@code @SessionAttributes} to the session. * Add {@link BindingResult} attributes where necessary. * @param request the current request * @param container contains the model to update * @throws Exception if creating BindingResult attributes fails */ public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception { ModelMap defaultModel = container.getDefaultModel(); if (container.getSessionStatus().isComplete()){ this.sessionAttributesHandler.cleanupAttributes(request); } else { this.sessionAttributesHandler.storeAttributes(request, defaultModel); } if (!container.isRequestHandled() && container.getModel() == defaultModel) { updateBindingResult(request, defaultModel); } }
/** * 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); } }); }
/** * Populate the model in the following order: * <ol> * <li>Retrieve "known" session attributes listed as {@code @SessionAttributes}. * <li>Invoke {@code @ModelAttribute} methods * <li>Find {@code @ModelAttribute} method arguments also listed as * {@code @SessionAttributes} and ensure they're present in the model raising * an exception if necessary. * </ol> * @param request the current request * @param container a container with the model to be initialized * @param handlerMethod the method for which the model is initialized * @throws Exception may arise from {@code @ModelAttribute} methods */ public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod) throws Exception { Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request); container.mergeAttributes(sessionAttributes); invokeModelAttributeMethods(request, container); for (String name : findSessionAttributeArguments(handlerMethod)) { if (!container.containsAttribute(name)) { Object value = this.sessionAttributesHandler.retrieveAttribute(request, name); if (value == null) { throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name); } container.addAttribute(name, value); } } }
private void runTest(Object controller) throws Exception { HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite(); resolvers.addResolver(new ModelAttributeMethodProcessor(false)); resolvers.addResolver(new ModelMethodProcessor()); WebDataBinderFactory dataBinderFactory = new DefaultDataBinderFactory(null); Class<?> type = controller.getClass(); Set<Method> methods = MethodIntrospector.selectMethods(type, METHOD_FILTER); List<InvocableHandlerMethod> modelMethods = new ArrayList<>(); for (Method method : methods) { InvocableHandlerMethod modelMethod = new InvocableHandlerMethod(controller, method); modelMethod.setHandlerMethodArgumentResolvers(resolvers); modelMethod.setDataBinderFactory(dataBinderFactory); modelMethods.add(modelMethod); } Collections.shuffle(modelMethods); SessionAttributesHandler sessionHandler = new SessionAttributesHandler(type, this.sessionAttributeStore); ModelFactory factory = new ModelFactory(modelMethods, dataBinderFactory, sessionHandler); factory.initModel(this.webRequest, this.mavContainer, new HandlerMethod(controller, "handle")); if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); for (String name : getInvokedMethods()) { sb.append(" >> ").append(name); } logger.debug(sb); } }
@Override protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest(request); // Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No HttpSession available -> no mutex necessary mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No synchronization on session demanded at all... mav = invokeHandlerMethod(request, response, handlerMethod); } if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; }