diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java index 2333e7d57c6..dd71eef9a1b 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java @@ -133,7 +133,7 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod * fashion. The behavior when one does not want concurrent jobs to be * executed is realized through adding the {@link StatefulJob} interface. * More information on stateful versus stateless jobs can be found - * here. + * here. *

The default setting is to run jobs concurrently. */ public void setConcurrent(boolean concurrent) { diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/package-info.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/package-info.java index a526bff2546..4b8a70a1821 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/package-info.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/package-info.java @@ -2,7 +2,7 @@ /** * * Support classes for the open source scheduler - * Quartz, + * Quartz, * allowing to set up Quartz Schedulers, JobDetails and * Triggers as beans in a Spring context. Also provides * convenience classes for implementing Quartz Jobs. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java index 5571532ed7d..68d2043269b 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java @@ -51,52 +51,52 @@ public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { } @Override - protected final void addInterceptors(InterceptorRegistry registry) { + protected void addInterceptors(InterceptorRegistry registry) { configurers.addInterceptors(registry); } @Override - protected final void addViewControllers(ViewControllerRegistry registry) { + protected void addViewControllers(ViewControllerRegistry registry) { configurers.addViewControllers(registry); } @Override - protected final void addResourceHandlers(ResourceHandlerRegistry registry) { + protected void addResourceHandlers(ResourceHandlerRegistry registry) { configurers.addResourceHandlers(registry); } @Override - protected final void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { + protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurers.configureDefaultServletHandling(configurer); } @Override - protected final void addArgumentResolvers(List argumentResolvers) { + protected void addArgumentResolvers(List argumentResolvers) { configurers.addArgumentResolvers(argumentResolvers); } @Override - protected final void addReturnValueHandlers(List returnValueHandlers) { + protected void addReturnValueHandlers(List returnValueHandlers) { configurers.addReturnValueHandlers(returnValueHandlers); } @Override - protected final void configureMessageConverters(List> converters) { + protected void configureMessageConverters(List> converters) { configurers.configureMessageConverters(converters); } @Override - protected final void addFormatters(FormatterRegistry registry) { + protected void addFormatters(FormatterRegistry registry) { configurers.addFormatters(registry); } @Override - protected final Validator getValidator() { + protected Validator getValidator() { return configurers.getValidator(); } @Override - protected final void configureHandlerExceptionResolvers(List exceptionResolvers) { + protected void configureHandlerExceptionResolvers(List exceptionResolvers) { configurers.configureHandlerExceptionResolvers(exceptionResolvers); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java index a47814c501b..be8fd6d6107 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java @@ -18,12 +18,14 @@ package org.springframework.web.servlet.handler; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -237,18 +239,16 @@ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMap * @see #handleNoMatch(Set, String, HttpServletRequest) */ protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { - List mappings = urlMap.get(lookupPath); - if (mappings == null) { - mappings = new ArrayList(handlerMethods.keySet()); - } - List matches = new ArrayList(); - - for (T mapping : mappings) { - T match = getMatchingMapping(mapping, request); - if (match != null) { - matches.add(new Match(match, handlerMethods.get(mapping))); - } + + List directPathMatches = this.urlMap.get(lookupPath); + if (directPathMatches != null) { + addMatchingMappings(directPathMatches, matches, request); + } + + if (matches.isEmpty()) { + // No choice but to go through all mappings + addMatchingMappings(this.handlerMethods.keySet(), matches, request); } if (!matches.isEmpty()) { @@ -279,6 +279,15 @@ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMap } } + private void addMatchingMappings(Collection mappings, List matches, HttpServletRequest request) { + for (T mapping : mappings) { + T match = getMatchingMapping(mapping, request); + if (match != null) { + matches.add(new Match(match, handlerMethods.get(mapping))); + } + } + } + /** * Check if a mapping matches the current request and return a (potentially * new) mapping with conditions relevant to the current request. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/RedirectView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/RedirectView.java index 184c9a32841..0e100ee7f34 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/RedirectView.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/RedirectView.java @@ -106,6 +106,7 @@ public class RedirectView extends AbstractUrlBasedView implements SmartView { private HttpStatus statusCode; + private boolean expandUriTemplateVariables = true; /** * Constructor for use as a bean. @@ -225,6 +226,18 @@ public class RedirectView extends AbstractUrlBasedView implements SmartView { this.statusCode = statusCode; } + /** + * Whether to treat the redirect URL as a URI template. + * Set this flag to false if the redirect URL contains open + * and close curly braces "{", "}" and you don't want them interpreted + * as URI variables. + *

Defaults to true. + * @param expandUriTemplateVariables + */ + public void setExpandUriTemplateVariables(boolean expandUriTemplateVariables) { + this.expandUriTemplateVariables = expandUriTemplateVariables; + } + /** * Returns "true" indicating this view performs a redirect. */ @@ -288,7 +301,7 @@ public class RedirectView extends AbstractUrlBasedView implements SmartView { enc = WebUtils.DEFAULT_CHARACTER_ENCODING; } - if (StringUtils.hasText(targetUrl)) { + if (this.expandUriTemplateVariables && StringUtils.hasText(targetUrl)) { Map variables = getCurrentRequestUriVariables(request); targetUrl = replaceUriTemplateVariables(targetUrl.toString(), model, variables, enc); } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java index 879961db773..ef81648a078 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java @@ -1182,6 +1182,19 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl assertEquals("myParam-42", response.getContentAsString()); } + // SPR-9062 + + @Test + public void ambiguousPathAndRequestMethod() throws Exception { + initServletWithControllers(AmbiguousPathAndRequestMethodController.class); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bug/EXISTING"); + MockHttpServletResponse response = new MockHttpServletResponse(); + getServlet().service(request, response); + assertEquals(200, response.getStatus()); + assertEquals("Pattern", response.getContentAsString()); + } + @Test public void bridgeMethods() throws Exception { initServletWithControllers(TestControllerImpl.class); @@ -2549,6 +2562,20 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl } } + @Controller + static class AmbiguousPathAndRequestMethodController { + + @RequestMapping(value = "/bug/EXISTING", method = RequestMethod.POST) + public void directMatch(Writer writer) throws IOException { + writer.write("Direct"); + } + + @RequestMapping(value = "/bug/{type}", method = RequestMethod.GET) + public void patternMatch(Writer writer) throws IOException { + writer.write("Pattern"); + } + } + @Controller @RequestMapping("/test*") public static class BindingCookieValueController { diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/RedirectViewUriTemplateTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/RedirectViewUriTemplateTests.java index b1e01024b21..87b5d5a3b85 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/RedirectViewUriTemplateTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/RedirectViewUriTemplateTests.java @@ -121,5 +121,17 @@ public class RedirectViewUriTemplateTests { assertEquals("", this.response.getRedirectedUrl()); } + + // SPR-9016 + + @Test + public void dontApplyUriVariables() throws Exception { + String url = "/test#{'one','abc'}"; + RedirectView redirectView = new RedirectView(url, true); + redirectView.setExpandUriTemplateVariables(false); + redirectView.renderMergedOutputModel(new ModelMap(), this.request, this.response); + + assertEquals(url, this.response.getRedirectedUrl()); + } } diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index eff2824e93e..c61372a70e7 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -27,6 +27,8 @@ Changes in version 3.1.1 (2012-02-06) * allow adding flash attributes in methods with a ModelAndView return value * preserve quotes in MediaType parameters * make flash attributes available in the model of ParameterizableViewController and UrlFilenameViewController +* add property to RedirectView to disable expanding URI variables in redirect URL +* fix request mapping bug involving direct vs pattern path matches with HTTP methods Changes in version 3.1 GA (2011-12-12) -------------------------------------- diff --git a/src/reference/docbook/scheduling.xml b/src/reference/docbook/scheduling.xml index 922a4a413fd..e85fa791125 100644 --- a/src/reference/docbook/scheduling.xml +++ b/src/reference/docbook/scheduling.xml @@ -21,7 +21,7 @@ Spring also features integration classes for supporting scheduling with the Timer, part of the JDK since 1.3, and the Quartz Scheduler (). Both of those + url="http://quartz-scheduler.org">). Both of those schedulers are set up using a FactoryBean with optional references to Timer or Trigger instances, respectively. Furthermore, a @@ -641,13 +641,13 @@ public class SampleBeanInititalizer {

- Using the OpenSymphony Quartz Scheduler + Using the Quartz Scheduler Quartz uses Trigger, Job and JobDetail objects to realize scheduling of all kinds of jobs. For the basic concepts behind Quartz, have a look at . For convenience + url="http://quartz-scheduler.org">. For convenience purposes, Spring offers a couple of classes that simplify the usage of Quartz within Spring-based applications.