diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/MissingPortletRequestParameterException.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/MissingPortletRequestParameterException.java
index 05f7e7a5572..34a1510dd1d 100644
--- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/MissingPortletRequestParameterException.java
+++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/MissingPortletRequestParameterException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2007 the original author or authors.
+ * Copyright 2002-2009 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.
@@ -17,16 +17,16 @@
package org.springframework.web.portlet.bind;
/**
- * PortletRequestBindingException subclass that indicates a missing parameter.
+ * {@link PortletRequestBindingException} subclass that indicates a missing parameter.
*
* @author Juergen Hoeller
* @since 2.0.2
*/
public class MissingPortletRequestParameterException extends PortletRequestBindingException {
- private String parameterName;
+ private final String parameterName;
- private String parameterType;
+ private final String parameterType;
/**
@@ -49,14 +49,14 @@ public class MissingPortletRequestParameterException extends PortletRequestBindi
/**
* Return the name of the offending parameter.
*/
- public String getParameterName() {
+ public final String getParameterName() {
return this.parameterName;
}
/**
* Return the expected type of the offending parameter.
*/
- public String getParameterType() {
+ public final String getParameterType() {
return this.parameterType;
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/MissingServletRequestParameterException.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/MissingServletRequestParameterException.java
index ea723d23643..fdd22f97547 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/MissingServletRequestParameterException.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/MissingServletRequestParameterException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2007 the original author or authors.
+ * Copyright 2002-2009 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.
@@ -24,9 +24,9 @@ package org.springframework.web.bind;
*/
public class MissingServletRequestParameterException extends ServletRequestBindingException {
- private String parameterName;
+ private final String parameterName;
- private String parameterType;
+ private final String parameterType;
/**
@@ -49,14 +49,14 @@ public class MissingServletRequestParameterException extends ServletRequestBindi
/**
* Return the name of the offending parameter.
*/
- public String getParameterName() {
+ public final String getParameterName() {
return this.parameterName;
}
/**
* Return the expected type of the offending parameter.
*/
- public String getParameterType() {
+ public final String getParameterType() {
return this.parameterType;
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/UnsatisfiedServletRequestParameterException.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/UnsatisfiedServletRequestParameterException.java
new file mode 100644
index 00000000000..8cd7d019bca
--- /dev/null
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/bind/UnsatisfiedServletRequestParameterException.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2002-2009 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;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link ServletRequestBindingException} subclass that indicates an unsatisfied
+ * parameter condition, as typically expressed using an @RequestMapping
+ * annotation at the @Controller type level.
+ *
+ * @author Juergen Hoeller
+ * @since 3.0
+ * @see org.springframework.web.bind.annotation.RequestMapping#params()
+ */
+public class UnsatisfiedServletRequestParameterException extends ServletRequestBindingException {
+
+ private final String[] paramConditions;
+
+ private final Map actualParams;
+
+
+ /**
+ * Create a new UnsatisfiedServletRequestParameterException.
+ * @param paramConditions the parameter conditions that have been violated
+ * @param actualParams the actual parameter Map associated with the ServletRequest
+ */
+ @SuppressWarnings("unchecked")
+ public UnsatisfiedServletRequestParameterException(String[] paramConditions, Map actualParams) {
+ super("");
+ this.paramConditions = paramConditions;
+ this.actualParams = (Map) actualParams;
+ }
+
+
+ @Override
+ public String getMessage() {
+ return "Parameter conditions \"" + StringUtils.arrayToDelimitedString(this.paramConditions, ", ") +
+ "\" not met for actual request parameters: " + requestParameterMapToString(this.actualParams);
+ }
+
+ private static String requestParameterMapToString(Map actualParams) {
+ StringBuilder result = new StringBuilder();
+ for (Iterator> it = actualParams.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = it.next();
+ result.append(entry.getKey()).append('=').append(ObjectUtils.nullSafeToString(entry.getValue()));
+ if (it.hasNext()) {
+ result.append(", ");
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Return the parameter conditions that have been violated.
+ * @see org.springframework.web.bind.annotation.RequestMapping#params()
+ */
+ public final String[] getParamConditions() {
+ return this.paramConditions;
+ }
+
+ /**
+ * Return the actual parameter Map associated with the ServletRequest.
+ * @see javax.servlet.ServletRequest#getParameterMap()
+ */
+ public final Map getActualParams() {
+ return this.actualParams;
+ }
+
+}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java
index 2ed0bfa1c10..3137d732047 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * Copyright 2002-2009 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.
@@ -21,7 +21,6 @@ import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
-import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
@@ -30,6 +29,7 @@ import org.springframework.stereotype.Controller;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.UnsatisfiedServletRequestParameterException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping;
@@ -172,9 +172,11 @@ public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandler
*/
@Override
protected void validateHandler(Object handler, HttpServletRequest request) throws Exception {
- RequestMapping mapping = this.cachedMappings.get(handler.getClass());
+ Class handlerClass = (handler instanceof String ?
+ getApplicationContext().getType((String) handler) : handler.getClass());
+ RequestMapping mapping = this.cachedMappings.get(handlerClass);
if (mapping == null) {
- mapping = AnnotationUtils.findAnnotation(handler.getClass(), RequestMapping.class);
+ mapping = AnnotationUtils.findAnnotation(handlerClass, RequestMapping.class);
}
if (mapping != null) {
validateMapping(mapping, request);
@@ -200,12 +202,8 @@ public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandler
String[] mappedParams = mapping.params();
if (!ServletAnnotationMappingUtils.checkParameters(mappedParams, request)) {
- throw new ServletException("Parameter conditions {" +
- StringUtils.arrayToDelimitedString(mappedParams, ", ") +
- "} not met for request parameters: " + request.getParameterMap());
+ throw new UnsatisfiedServletRequestParameterException(mappedParams, request.getParameterMap());
}
}
-
-
}
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java
index 2d184e3d90f..0740744aef2 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * Copyright 2002-2009 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.
@@ -584,6 +584,66 @@ public class ServletAnnotationControllerTests {
assertEquals("mySurpriseView", response.getContentAsString());
}
+ @Test
+ public void constrainedParameterDispatchingController() throws Exception {
+ final MockServletContext servletContext = new MockServletContext();
+ final MockServletConfig servletConfig = new MockServletConfig(servletContext);
+
+ @SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
+ @Override
+ protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
+ GenericWebApplicationContext wac = new GenericWebApplicationContext();
+ wac.setServletContext(servletContext);
+ RootBeanDefinition bd = new RootBeanDefinition(MyConstrainedParameterDispatchingController.class);
+ bd.setScope(WebApplicationContext.SCOPE_REQUEST);
+ wac.registerBeanDefinition("controller", bd);
+ wac.refresh();
+ return wac;
+ }
+ };
+ servlet.init(servletConfig);
+
+ MockHttpServletRequest request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do");
+ request.addParameter("view", "other");
+ MockHttpServletResponse response = new MockHttpServletResponse();
+ try {
+ servlet.service(request, response);
+ fail("Should have failed because of type-level parameter constraint not met");
+ }
+ catch (ServletException ex) {
+ // expected
+ ex.printStackTrace();
+ }
+
+ request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do");
+ request.addParameter("active", "true");
+ request.addParameter("view", "other");
+ response = new MockHttpServletResponse();
+ servlet.service(request, response);
+ assertEquals("myOtherView", response.getContentAsString());
+
+ request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do");
+ request.addParameter("view", "my");
+ request.addParameter("lang", "de");
+ response = new MockHttpServletResponse();
+ try {
+ servlet.service(request, response);
+ fail("Should have failed because of type-level parameter constraint not met");
+ }
+ catch (ServletException ex) {
+ // expected
+ ex.printStackTrace();
+ }
+
+ request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do");
+ request.addParameter("view", "my");
+ request.addParameter("lang", "de");
+ request.addParameter("active", "true");
+ response = new MockHttpServletResponse();
+ servlet.service(request, response);
+ assertEquals("myLangView", response.getContentAsString());
+ }
+
@Test
public void methodNameDispatchingController() throws Exception {
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
@@ -1189,6 +1249,22 @@ public class ServletAnnotationControllerTests {
}
+ @Controller
+ @RequestMapping(value = "/myPath.do", params = {"active"})
+ private static class MyConstrainedParameterDispatchingController {
+
+ @RequestMapping(params = {"view", "!lang"})
+ public void myOtherHandle(HttpServletResponse response) throws IOException {
+ response.getWriter().write("myOtherView");
+ }
+
+ @RequestMapping(method = RequestMethod.GET, params = {"view=my", "lang=de"})
+ public void myLangHandle(HttpServletResponse response) throws IOException {
+ response.getWriter().write("myLangView");
+ }
+ }
+
+
@Controller
@RequestMapping(value = "/*.do", method = RequestMethod.POST, params = "myParam=myValue")
private static class MyPostMethodNameDispatchingController extends MethodNameDispatchingController {