Rossen Stoyanchev
13 years ago
15 changed files with 1104 additions and 540 deletions
@ -0,0 +1,112 @@
@@ -0,0 +1,112 @@
|
||||
/* |
||||
* 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.servlet.mvc.method.annotation; |
||||
|
||||
import static junit.framework.Assert.assertNotNull; |
||||
|
||||
import javax.servlet.ServletException; |
||||
|
||||
import org.junit.After; |
||||
import org.springframework.beans.factory.support.RootBeanDefinition; |
||||
import org.springframework.context.ApplicationContextInitializer; |
||||
import org.springframework.mock.web.MockServletConfig; |
||||
import org.springframework.web.context.WebApplicationContext; |
||||
import org.springframework.web.context.support.GenericWebApplicationContext; |
||||
import org.springframework.web.servlet.DispatcherServlet; |
||||
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver; |
||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; |
||||
|
||||
/** |
||||
* Base class for tests using on the DispatcherServlet and HandlerMethod infrastructure classes: |
||||
* <ul> |
||||
* <li>RequestMappingHandlerMapping |
||||
* <li>RequestMappingHandlerAdapter |
||||
* <li>ExceptionHandlerExceptionResolver |
||||
* </ul> |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class AbstractServletHandlerMethodTests { |
||||
|
||||
private DispatcherServlet servlet; |
||||
|
||||
@After |
||||
public void tearDown() { |
||||
this.servlet = null; |
||||
} |
||||
|
||||
protected DispatcherServlet getServlet() { |
||||
assertNotNull("DispatcherServlet not initialized", servlet); |
||||
return servlet; |
||||
} |
||||
|
||||
/** |
||||
* Initialize a DispatcherServlet instance registering zero or more controller classes. |
||||
*/ |
||||
protected WebApplicationContext initServletWithControllers(final Class<?>... controllerClasses) |
||||
throws ServletException { |
||||
return initServlet(null, controllerClasses); |
||||
} |
||||
|
||||
/** |
||||
* Initialize a DispatcherServlet instance registering zero or more controller classes |
||||
* and also providing additional bean definitions through a callback. |
||||
*/ |
||||
@SuppressWarnings("serial") |
||||
protected WebApplicationContext initServlet( |
||||
final ApplicationContextInitializer<GenericWebApplicationContext> initializer, |
||||
final Class<?>... controllerClasses) throws ServletException { |
||||
|
||||
final GenericWebApplicationContext wac = new GenericWebApplicationContext(); |
||||
|
||||
servlet = new DispatcherServlet() { |
||||
@Override |
||||
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { |
||||
for (Class<?> clazz : controllerClasses) { |
||||
wac.registerBeanDefinition(clazz.getSimpleName(), new RootBeanDefinition(clazz)); |
||||
} |
||||
|
||||
Class<?> mappingType = RequestMappingHandlerMapping.class; |
||||
wac.registerBeanDefinition("handlerMapping", new RootBeanDefinition(mappingType)); |
||||
|
||||
Class<?> adapterType = RequestMappingHandlerAdapter.class; |
||||
wac.registerBeanDefinition("handlerAdapter", new RootBeanDefinition(adapterType)); |
||||
|
||||
Class<?> resolverType = ExceptionHandlerExceptionResolver.class; |
||||
wac.registerBeanDefinition("requestMappingResolver", new RootBeanDefinition(resolverType)); |
||||
|
||||
resolverType = ResponseStatusExceptionResolver.class; |
||||
wac.registerBeanDefinition("responseStatusResolver", new RootBeanDefinition(resolverType)); |
||||
|
||||
resolverType = DefaultHandlerExceptionResolver.class; |
||||
wac.registerBeanDefinition("defaultResolver", new RootBeanDefinition(resolverType)); |
||||
|
||||
if (initializer != null) { |
||||
initializer.initialize(wac); |
||||
} |
||||
|
||||
wac.refresh(); |
||||
return wac; |
||||
} |
||||
}; |
||||
|
||||
servlet.init(new MockServletConfig()); |
||||
|
||||
return wac; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,215 @@
@@ -0,0 +1,215 @@
|
||||
/* |
||||
* 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.servlet.mvc.method.annotation; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertNull; |
||||
|
||||
import java.util.Locale; |
||||
import java.util.Map; |
||||
|
||||
import javax.servlet.ServletException; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.beans.factory.support.RootBeanDefinition; |
||||
import org.springframework.context.ApplicationContextInitializer; |
||||
import org.springframework.mock.web.MockHttpServletRequest; |
||||
import org.springframework.mock.web.MockHttpServletResponse; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.ui.Model; |
||||
import org.springframework.web.bind.annotation.FlashAttributes; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RequestMethod; |
||||
import org.springframework.web.bind.support.FlashStatus; |
||||
import org.springframework.web.context.WebApplicationContext; |
||||
import org.springframework.web.context.support.GenericWebApplicationContext; |
||||
import org.springframework.web.method.annotation.FlashAttributesHandler; |
||||
import org.springframework.web.servlet.View; |
||||
import org.springframework.web.servlet.ViewResolver; |
||||
|
||||
/** |
||||
* Test controllers with @{@link FlashAttributes} through the DispatcherServlet. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class FlashAttributesServletTests extends AbstractServletHandlerMethodTests { |
||||
|
||||
private static final String MESSAGE_KEY = "message"; |
||||
|
||||
@Test |
||||
public void successMessage() throws Exception { |
||||
|
||||
initServletWithModelExposingViewResolver(MessageController.class); |
||||
|
||||
MockHttpServletRequest req = new MockHttpServletRequest("GET", "/message"); |
||||
MockHttpServletResponse res = new MockHttpServletResponse(); |
||||
getServlet().service(req, res); |
||||
|
||||
assertEquals(200, res.getStatus()); |
||||
assertNull(getModelAttribute(req, MESSAGE_KEY)); |
||||
assertNull(getFlashAttribute(req, MESSAGE_KEY)); |
||||
|
||||
req.setMethod("POST"); |
||||
getServlet().service(req, res); |
||||
|
||||
assertEquals(200, res.getStatus()); |
||||
assertEquals("Yay!", ((Message) getModelAttribute(req, MESSAGE_KEY)).getText()); |
||||
assertEquals("Yay!", ((Message) getFlashAttribute(req, MESSAGE_KEY)).getText()); |
||||
|
||||
req.setMethod("GET"); |
||||
getServlet().service(req, res); |
||||
|
||||
assertEquals(200, res.getStatus()); |
||||
assertEquals("Yay!", ((Message) getModelAttribute(req, MESSAGE_KEY)).getText()); |
||||
assertNull(getFlashAttribute(req, MESSAGE_KEY)); |
||||
} |
||||
|
||||
@Test |
||||
public void successMessageAcrossControllers() throws Exception { |
||||
|
||||
initServletWithModelExposingViewResolver(MessageController.class, SecondMessageController.class); |
||||
|
||||
MockHttpServletRequest req = new MockHttpServletRequest("POST", "/message"); |
||||
MockHttpServletResponse res = new MockHttpServletResponse(); |
||||
getServlet().service(req, res); |
||||
|
||||
req.setParameter("another", "true"); |
||||
getServlet().service(req, res); |
||||
|
||||
assertEquals(200, res.getStatus()); |
||||
assertEquals("Nay!", ((Message) getModelAttribute(req, MESSAGE_KEY)).getText()); |
||||
assertEquals("Nay!", ((Message) getFlashAttribute(req, MESSAGE_KEY)).getText()); |
||||
|
||||
req.setMethod("GET"); |
||||
req.setRequestURI("/second/message"); |
||||
getServlet().service(req, res); |
||||
|
||||
assertEquals(200, res.getStatus()); |
||||
assertEquals("Nay!", ((Message) getModelAttribute(req, MESSAGE_KEY)).getText()); |
||||
assertNull(getFlashAttribute(req, MESSAGE_KEY)); |
||||
} |
||||
|
||||
@Controller |
||||
@FlashAttributes("message") |
||||
static class MessageController { |
||||
|
||||
@RequestMapping(value="/message", method=RequestMethod.GET) |
||||
public void message(Model model) { |
||||
} |
||||
|
||||
@RequestMapping(value="/message", method=RequestMethod.POST) |
||||
public String sendMessage(Model model, FlashStatus status) { |
||||
status.setActive(); |
||||
model.addAttribute(Message.success("Yay!")); |
||||
return "redirect:/message"; |
||||
} |
||||
|
||||
@RequestMapping(value="/message", method=RequestMethod.POST, params="another") |
||||
public String sendMessageToSecondController(Model model, FlashStatus status) { |
||||
status.setActive(); |
||||
model.addAttribute(Message.error("Nay!")); |
||||
return "redirect:/second/message"; |
||||
} |
||||
} |
||||
|
||||
@Controller |
||||
static class SecondMessageController { |
||||
|
||||
@RequestMapping(value="/second/message", method=RequestMethod.GET) |
||||
public void message(Model model) { |
||||
} |
||||
} |
||||
|
||||
private static class Message { |
||||
|
||||
private final MessageType type; |
||||
|
||||
private final String text; |
||||
|
||||
private Message(MessageType type, String text) { |
||||
this.type = type; |
||||
this.text = text; |
||||
} |
||||
|
||||
public static Message success(String text) { |
||||
return new Message(MessageType.success, text); |
||||
} |
||||
|
||||
public static Message error(String text) { |
||||
return new Message(MessageType.error, text); |
||||
} |
||||
|
||||
public MessageType getType() { |
||||
return type; |
||||
} |
||||
|
||||
public String getText() { |
||||
return text; |
||||
} |
||||
|
||||
public String toString() { |
||||
return type + ": " + text; |
||||
} |
||||
|
||||
} |
||||
|
||||
private static enum MessageType { |
||||
info, success, warning, error |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private Object getModelAttribute(MockHttpServletRequest req, String key) { |
||||
Map<String, ?> model = (Map<String, ?>) req.getAttribute(ModelExposingViewResolver.REQUEST_ATTRIBITE_MODEL); |
||||
return model.get(key); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private Object getFlashAttribute(MockHttpServletRequest req, String key) { |
||||
String flashAttributesKey = FlashAttributesHandler.FLASH_ATTRIBUTES_SESSION_KEY; |
||||
Map<String, Object> attrs = (Map<String, Object>) req.getSession().getAttribute(flashAttributesKey); |
||||
return (attrs != null) ? attrs.get(key) : null; |
||||
} |
||||
|
||||
private WebApplicationContext initServletWithModelExposingViewResolver(Class<?>... controllerClasses) |
||||
throws ServletException { |
||||
|
||||
return initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() { |
||||
public void initialize(GenericWebApplicationContext wac) { |
||||
wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(ModelExposingViewResolver.class)); |
||||
} |
||||
}, controllerClasses); |
||||
} |
||||
|
||||
static class ModelExposingViewResolver implements ViewResolver { |
||||
|
||||
static String REQUEST_ATTRIBITE_MODEL = "ModelExposingViewResolver.model"; |
||||
|
||||
public View resolveViewName(final String viewName, Locale locale) throws Exception { |
||||
return new View() { |
||||
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { |
||||
request.setAttribute(REQUEST_ATTRIBITE_MODEL, model); |
||||
} |
||||
public String getContentType() { |
||||
return null; |
||||
} |
||||
}; |
||||
} |
||||
} |
||||
|
||||
} |
File diff suppressed because it is too large
Load Diff
189
org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/UriTemplateServletHandlerMethodTests.java → org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/UriTemplateServletAnnotationControllerHandlerMethodTests.java
189
org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/UriTemplateServletHandlerMethodTests.java → org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/UriTemplateServletAnnotationControllerHandlerMethodTests.java
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
/* |
||||
* Copyright 2002-2010 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.bind.annotation; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Inherited; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
/** |
||||
* Annotation that indicates what attributes should be stored in the session or in |
||||
* some conversational storage in order to survive a client-side redirect. |
||||
* |
||||
* TODO ... |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 3.1 |
||||
* |
||||
* @see org.springframework.web.bind.support.FlashStatus |
||||
*/ |
||||
@Target({ElementType.TYPE}) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Inherited |
||||
@Documented |
||||
public @interface FlashAttributes { |
||||
|
||||
/** |
||||
* The names of flash attributes in the model to be stored. |
||||
* |
||||
* TODO ... |
||||
*/ |
||||
String[] value() default {}; |
||||
|
||||
/** |
||||
* TODO ... |
||||
* |
||||
*/ |
||||
Class[] types() default {}; |
||||
|
||||
} |
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
/* |
||||
* 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.bind.support; |
||||
|
||||
import org.springframework.web.bind.annotation.FlashAttributes; |
||||
|
||||
|
||||
/** |
||||
* Simple interface to pass into controller methods to allow them to activate |
||||
* a mode in which model attributes identified as "flash attributes" are |
||||
* temporarily stored in the session to make them available to the next |
||||
* request. The most common scenario is a client-side redirect. |
||||
* |
||||
* <p>In active mode, model attributes that match the attribute names or |
||||
* types declared via @{@link FlashAttributes} are saved in the session. |
||||
* On the next request, any flash attributes found in the session are |
||||
* automatically added to the model of the target controller method and |
||||
* are also cleared from the session. |
||||
* |
||||
* TODO ... |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 3.1 |
||||
*/ |
||||
public interface FlashStatus { |
||||
|
||||
/** |
||||
* TODO ... |
||||
*/ |
||||
void setActive(); |
||||
|
||||
/** |
||||
* TODO ... |
||||
*/ |
||||
boolean isActive(); |
||||
|
||||
} |
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
/* |
||||
* 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.bind.support; |
||||
|
||||
/** |
||||
* TODO ... |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 3.1 |
||||
*/ |
||||
public class SimpleFlashStatus implements FlashStatus { |
||||
|
||||
private boolean active = false; |
||||
|
||||
public void setActive() { |
||||
this.active = true; |
||||
} |
||||
|
||||
public boolean isActive() { |
||||
return this.active; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,108 @@
@@ -0,0 +1,108 @@
|
||||
/* |
||||
* 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.HashSet; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils; |
||||
import org.springframework.web.bind.annotation.FlashAttributes; |
||||
import org.springframework.web.context.request.WebRequest; |
||||
|
||||
/** |
||||
* Manages flash attributes declared via @{@link FlashAttributes}. |
||||
* |
||||
* TODO ... |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 3.1 |
||||
*/ |
||||
public class FlashAttributesHandler { |
||||
|
||||
public static final String FLASH_ATTRIBUTES_SESSION_KEY = FlashAttributesHandler.class.getName() + ".attributes"; |
||||
|
||||
private final Set<String> attributeNames = new HashSet<String>(); |
||||
|
||||
private final Set<Class<?>> attributeTypes = new HashSet<Class<?>>(); |
||||
|
||||
/** |
||||
* TODO ... |
||||
*/ |
||||
public FlashAttributesHandler(Class<?> handlerType) { |
||||
FlashAttributes annotation = AnnotationUtils.findAnnotation(handlerType, FlashAttributes.class); |
||||
if (annotation != null) { |
||||
this.attributeNames.addAll(Arrays.asList(annotation.value())); |
||||
this.attributeTypes.addAll(Arrays.<Class<?>>asList(annotation.types())); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Whether the controller represented by this handler has declared flash |
||||
* attribute names or types via @{@link FlashAttributes}. |
||||
*/ |
||||
public boolean hasFlashAttributes() { |
||||
return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0)); |
||||
} |
||||
|
||||
/** |
||||
* TODO ... |
||||
*/ |
||||
public boolean isFlashAttribute(String attributeName, Class<?> attributeType) { |
||||
return (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)); |
||||
} |
||||
|
||||
/** |
||||
* TODO ... |
||||
*/ |
||||
public void storeAttributes(WebRequest request, Map<String, ?> attributes) { |
||||
Map<String, Object> filtered = filterAttributes(attributes); |
||||
if (!filtered.isEmpty()) { |
||||
request.setAttribute(FLASH_ATTRIBUTES_SESSION_KEY, filtered, WebRequest.SCOPE_SESSION); |
||||
} |
||||
} |
||||
|
||||
private Map<String, Object> filterAttributes(Map<String, ?> attributes) { |
||||
Map<String, Object> result = new LinkedHashMap<String, Object>(); |
||||
for (String name : attributes.keySet()) { |
||||
Object value = attributes.get(name); |
||||
Class<?> type = (value != null) ? value.getClass() : null; |
||||
if (isFlashAttribute(name, type)) { |
||||
result.put(name, value); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* TODO ... |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public Map<String, Object> retrieveAttributes(WebRequest request) { |
||||
return (Map<String, Object>) request.getAttribute(FLASH_ATTRIBUTES_SESSION_KEY, WebRequest.SCOPE_SESSION); |
||||
} |
||||
|
||||
/** |
||||
* TODO ... |
||||
*/ |
||||
public void cleanupAttributes(WebRequest request) { |
||||
request.removeAttribute(FLASH_ATTRIBUTES_SESSION_KEY, WebRequest.SCOPE_SESSION); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue