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.