Browse Source

Consistent Map/Set ordering

Use LinkedHashMaps/Sets wherever exposed to users, and code tests defensively in terms of expected Map/Set ordering. Otherwise, there'll be runtime order differences between JDK 7 and JDK 8 due to internal HashMap/Set implementation differences.

Issue: SPR-9639
pull/263/merge
Juergen Hoeller 12 years ago
parent
commit
9c09a0a037
  1. 31
      spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReaderTests.java
  2. 27
      spring-context/src/test/java/org/springframework/ui/ModelMapTests.java
  3. 5
      spring-context/src/test/java/org/springframework/validation/DataBinderTests.java
  4. 5
      spring-expression/src/test/java/org/springframework/expression/spel/testresources/Inventor.java
  5. 11
      spring-web/src/main/java/org/springframework/web/HttpMediaTypeException.java
  6. 6
      spring-web/src/main/java/org/springframework/web/HttpMediaTypeNotSupportedException.java
  7. 10
      spring-web/src/main/java/org/springframework/web/HttpRequestMethodNotSupportedException.java
  8. 8
      spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/handler/AbstractHandlerExceptionResolver.java
  9. 5
      spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/handler/SimpleMappingExceptionResolver.java
  10. 5
      spring-webmvc-portlet/src/test/java/org/springframework/web/portlet/handler/SimpleMappingExceptionResolverTests.java
  11. 6
      spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java
  12. 68
      spring-webmvc/src/main/java/org/springframework/web/servlet/handler/SimpleMappingExceptionResolver.java
  13. 22
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java
  14. 10
      spring-webmvc/src/main/java/org/springframework/web/servlet/view/AbstractView.java
  15. 4
      spring-webmvc/src/test/java/org/springframework/web/servlet/handler/SimpleMappingExceptionResolverTests.java
  16. 79
      spring-webmvc/src/test/java/org/springframework/web/servlet/view/document/PdfViewTests.java
  17. 20
      spring-webmvc/src/test/java/org/springframework/web/servlet/view/feed/AtomFeedViewTests.java
  18. 18
      spring-webmvc/src/test/java/org/springframework/web/servlet/view/feed/RssFeedViewTests.java

31
spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReaderTests.java

@ -29,7 +29,7 @@ import org.springframework.core.io.ClassPathResource; @@ -29,7 +29,7 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.tests.sample.beans.TestBean;
import org.springframework.util.ObjectUtils;
/**
* @author Rick Evans
@ -38,13 +38,13 @@ import org.springframework.tests.sample.beans.TestBean; @@ -38,13 +38,13 @@ import org.springframework.tests.sample.beans.TestBean;
public class XmlBeanDefinitionReaderTests extends TestCase {
public void testSetParserClassSunnyDay() {
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();;
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
new XmlBeanDefinitionReader(registry).setDocumentReaderClass(DefaultBeanDefinitionDocumentReader.class);
}
public void testSetParserClassToNull() {
try {
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();;
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
new XmlBeanDefinitionReader(registry).setDocumentReaderClass(null);
fail("Should have thrown IllegalArgumentException (null parserClass)");
}
@ -54,7 +54,7 @@ public class XmlBeanDefinitionReaderTests extends TestCase { @@ -54,7 +54,7 @@ public class XmlBeanDefinitionReaderTests extends TestCase {
public void testSetParserClassToUnsupportedParserType() throws Exception {
try {
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();;
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
new XmlBeanDefinitionReader(registry).setDocumentReaderClass(String.class);
fail("Should have thrown IllegalArgumentException (unsupported parserClass)");
}
@ -64,7 +64,7 @@ public class XmlBeanDefinitionReaderTests extends TestCase { @@ -64,7 +64,7 @@ public class XmlBeanDefinitionReaderTests extends TestCase {
public void testWithOpenInputStream() {
try {
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();;
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
Resource resource = new InputStreamResource(getClass().getResourceAsStream("test.xml"));
new XmlBeanDefinitionReader(registry).loadBeanDefinitions(resource);
fail("Should have thrown BeanDefinitionStoreException (can't determine validation mode)");
@ -74,7 +74,7 @@ public class XmlBeanDefinitionReaderTests extends TestCase { @@ -74,7 +74,7 @@ public class XmlBeanDefinitionReaderTests extends TestCase {
}
public void testWithOpenInputStreamAndExplicitValidationMode() {
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();;
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
Resource resource = new InputStreamResource(getClass().getResourceAsStream("test.xml"));
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_DTD);
@ -83,14 +83,14 @@ public class XmlBeanDefinitionReaderTests extends TestCase { @@ -83,14 +83,14 @@ public class XmlBeanDefinitionReaderTests extends TestCase {
}
public void testWithImport() {
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();;
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
Resource resource = new ClassPathResource("import.xml", getClass());
new XmlBeanDefinitionReader(registry).loadBeanDefinitions(resource);
testBeanDefinitions(registry);
}
public void testWithWildcardImport() {
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();;
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
Resource resource = new ClassPathResource("importPattern.xml", getClass());
new XmlBeanDefinitionReader(registry).loadBeanDefinitions(resource);
testBeanDefinitions(registry);
@ -98,7 +98,7 @@ public class XmlBeanDefinitionReaderTests extends TestCase { @@ -98,7 +98,7 @@ public class XmlBeanDefinitionReaderTests extends TestCase {
public void testWithInputSource() {
try {
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();;
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
InputSource resource = new InputSource(getClass().getResourceAsStream("test.xml"));
new XmlBeanDefinitionReader(registry).loadBeanDefinitions(resource);
fail("Should have thrown BeanDefinitionStoreException (can't determine validation mode)");
@ -108,7 +108,7 @@ public class XmlBeanDefinitionReaderTests extends TestCase { @@ -108,7 +108,7 @@ public class XmlBeanDefinitionReaderTests extends TestCase {
}
public void testWithInputSourceAndExplicitValidationMode() {
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();;
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
InputSource resource = new InputSource(getClass().getResourceAsStream("test.xml"));
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_DTD);
@ -117,7 +117,7 @@ public class XmlBeanDefinitionReaderTests extends TestCase { @@ -117,7 +117,7 @@ public class XmlBeanDefinitionReaderTests extends TestCase {
}
public void testWithFreshInputStream() {
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();;
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
Resource resource = new ClassPathResource("test.xml", getClass());
new XmlBeanDefinitionReader(registry).loadBeanDefinitions(resource);
testBeanDefinitions(registry);
@ -133,9 +133,10 @@ public class XmlBeanDefinitionReaderTests extends TestCase { @@ -133,9 +133,10 @@ public class XmlBeanDefinitionReaderTests extends TestCase {
assertEquals(TestBean.class.getName(), registry.getBeanDefinition("rod").getBeanClassName());
assertEquals(TestBean.class.getName(), registry.getBeanDefinition("aliased").getBeanClassName());
assertTrue(registry.isAlias("youralias"));
assertEquals(2, registry.getAliases("aliased").length);
assertEquals("myalias", registry.getAliases("aliased")[0]);
assertEquals("youralias", registry.getAliases("aliased")[1]);
String[] aliases = registry.getAliases("aliased");
assertEquals(2, aliases.length);
assertTrue(ObjectUtils.containsElement(aliases, "myalias"));
assertTrue(ObjectUtils.containsElement(aliases, "youralias"));
}
public void testDtdValidationAutodetect() throws Exception {
@ -147,7 +148,7 @@ public class XmlBeanDefinitionReaderTests extends TestCase { @@ -147,7 +148,7 @@ public class XmlBeanDefinitionReaderTests extends TestCase {
}
private void doTestValidation(String resourceName) throws Exception {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();;
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
Resource resource = new ClassPathResource(resourceName, getClass());
new XmlBeanDefinitionReader(factory).loadBeanDefinitions(resource);
TestBean bean = (TestBean) factory.getBean("testBean");

27
spring-context/src/test/java/org/springframework/ui/ModelMapTests.java

@ -16,26 +16,26 @@ @@ -16,26 +16,26 @@
package org.springframework.ui;
import static org.junit.Assert.*;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.tests.sample.beans.TestBean;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import static org.junit.Assert.*;
/**
* @author Rick Evans
* @author Juergen Hoeller
@ -226,12 +226,12 @@ public final class ModelMapTests { @@ -226,12 +226,12 @@ public final class ModelMapTests {
public void testAopCglibProxy() throws Exception {
ModelMap map = new ModelMap();
ProxyFactory factory = new ProxyFactory();
Date date = new Date();
factory.setTarget(date);
SomeInnerClass val = new SomeInnerClass();
factory.setTarget(val);
factory.setProxyTargetClass(true);
map.addAttribute(factory.getProxy());
assertTrue(map.containsKey("date"));
assertEquals(date, map.get("date"));
assertTrue(map.containsKey("someInnerClass"));
assertEquals(val, map.get("someInnerClass"));
}
@Test
@ -288,11 +288,20 @@ public final class ModelMapTests { @@ -288,11 +288,20 @@ public final class ModelMapTests {
}
private static class SomeInnerClass {
public static class SomeInnerClass {
public boolean equals(Object obj) {
return (obj instanceof SomeInnerClass);
}
@Override
public int hashCode() {
return SomeInnerClass.class.hashCode();
}
}
private static class UKInnerClass {
public static class UKInnerClass {
}
}

5
spring-context/src/test/java/org/springframework/validation/DataBinderTests.java

@ -56,6 +56,7 @@ import org.springframework.core.convert.support.DefaultConversionService; @@ -56,6 +56,7 @@ import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.format.Formatter;
import org.springframework.format.number.NumberFormatter;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
@ -614,8 +615,8 @@ public class DataBinderTests extends TestCase { @@ -614,8 +615,8 @@ public class DataBinderTests extends TestCase {
assertNull(rod.getSomeMap().get("key4"));
String[] disallowedFields = binder.getBindingResult().getSuppressedFields();
assertEquals(2, disallowedFields.length);
assertEquals("someMap[key3]", disallowedFields[0]);
assertEquals("someMap[key4]", disallowedFields[1]);
assertTrue(ObjectUtils.containsElement(disallowedFields, "someMap[key3]"));
assertTrue(ObjectUtils.containsElement(disallowedFields, "someMap[key4]"));
}
/**

5
spring-expression/src/test/java/org/springframework/expression/spel/testresources/Inventor.java

@ -3,6 +3,7 @@ package org.springframework.expression.spel.testresources; @@ -3,6 +3,7 @@ package org.springframework.expression.spel.testresources;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -30,8 +31,8 @@ public class Inventor { @@ -30,8 +31,8 @@ public class Inventor {
private boolean accessedThroughGetSet;
public List<Integer> listOfInteger = new ArrayList<Integer>();
public List<Boolean> booleanList = new ArrayList<Boolean>();
public Map<String,Boolean> mapOfStringToBoolean = new HashMap<String,Boolean>();
public Map<Integer,String> mapOfNumbersUpToTen = new HashMap<Integer,String>();
public Map<String,Boolean> mapOfStringToBoolean = new LinkedHashMap<String,Boolean>();
public Map<Integer,String> mapOfNumbersUpToTen = new LinkedHashMap<Integer,String>();
public List<Integer> listOfNumbersUpToTen = new ArrayList<Integer>();
public List<Integer> listOneFive = new ArrayList<Integer>();
public String[] stringArrayOfThreeItems = new String[]{"1","2","3"};

11
spring-web/src/main/java/org/springframework/web/HttpMediaTypeException.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@ -16,8 +16,8 @@ @@ -16,8 +16,8 @@
package org.springframework.web;
import java.util.List;
import java.util.Collections;
import java.util.List;
import javax.servlet.ServletException;
import org.springframework.http.MediaType;
@ -33,6 +33,7 @@ public abstract class HttpMediaTypeException extends ServletException { @@ -33,6 +33,7 @@ public abstract class HttpMediaTypeException extends ServletException {
private final List<MediaType> supportedMediaTypes;
/**
* Create a new HttpMediaTypeException.
* @param message the exception message
@ -48,13 +49,15 @@ public abstract class HttpMediaTypeException extends ServletException { @@ -48,13 +49,15 @@ public abstract class HttpMediaTypeException extends ServletException {
*/
protected HttpMediaTypeException(String message, List<MediaType> supportedMediaTypes) {
super(message);
this.supportedMediaTypes = supportedMediaTypes;
this.supportedMediaTypes = Collections.unmodifiableList(supportedMediaTypes);
}
/**
* Return the list of supported media types.
*/
public List<MediaType> getSupportedMediaTypes() {
return supportedMediaTypes;
return this.supportedMediaTypes;
}
}

6
spring-web/src/main/java/org/springframework/web/HttpMediaTypeNotSupportedException.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@ -32,6 +32,7 @@ public class HttpMediaTypeNotSupportedException extends HttpMediaTypeException { @@ -32,6 +32,7 @@ public class HttpMediaTypeNotSupportedException extends HttpMediaTypeException {
private final MediaType contentType;
/**
* Create a new HttpMediaTypeNotSupportedException.
* @param message the exception message
@ -61,11 +62,12 @@ public class HttpMediaTypeNotSupportedException extends HttpMediaTypeException { @@ -61,11 +62,12 @@ public class HttpMediaTypeNotSupportedException extends HttpMediaTypeException {
this.contentType = contentType;
}
/**
* Return the HTTP request content type method that caused the failure.
*/
public MediaType getContentType() {
return contentType;
return this.contentType;
}
}

10
spring-web/src/main/java/org/springframework/web/HttpRequestMethodNotSupportedException.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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,9 +17,9 @@ @@ -17,9 +17,9 @@
package org.springframework.web;
import java.util.Collection;
import java.util.HashSet;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.servlet.ServletException;
import org.springframework.http.HttpMethod;
@ -105,11 +105,11 @@ public class HttpRequestMethodNotSupportedException extends ServletException { @@ -105,11 +105,11 @@ public class HttpRequestMethodNotSupportedException extends ServletException {
* Return the actually supported HTTP methods, if known, as {@link HttpMethod} instances.
*/
public Set<HttpMethod> getSupportedHttpMethods() {
Set<HttpMethod> supportedMethods = new HashSet<HttpMethod>();
Set<HttpMethod> supportedMethods = new LinkedHashSet<HttpMethod>();
for (String value : this.supportedMethods) {
supportedMethods.add(HttpMethod.valueOf(value));
}
return supportedMethods;
return Collections.unmodifiableSet(supportedMethods);
}
}

8
spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/handler/AbstractHandlerExceptionResolver.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@ -48,7 +48,7 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti @@ -48,7 +48,7 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
private int order = Ordered.LOWEST_PRECEDENCE;
private Set mappedHandlers;
private Set<?> mappedHandlers;
private Class[] mappedHandlerClasses;
@ -74,7 +74,7 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti @@ -74,7 +74,7 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
* error view will be used as fallback for all exceptions; any further
* HandlerExceptionResolvers in the chain will be ignored in this case.
*/
public void setMappedHandlers(Set mappedHandlers) {
public void setMappedHandlers(Set<?> mappedHandlers) {
this.mappedHandlers = mappedHandlers;
}
@ -145,7 +145,7 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti @@ -145,7 +145,7 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
/**
* Check whether this resolver is supposed to apply to the given handler.
* <p>The default implementation checks against the specified mapped handlers
* and handler classes, if any, and alspo checks the window state (according
* and handler classes, if any, and also checks the window state (according
* to the "renderWhenMinimize" property).
* @param request current portlet request
* @param handler the executed handler, or {@code null} if none chosen at the

5
spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/handler/SimpleMappingExceptionResolver.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@ -159,7 +159,8 @@ public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionReso @@ -159,7 +159,8 @@ public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionReso
for (Enumeration names = exceptionMappings.propertyNames(); names.hasMoreElements();) {
String exceptionMapping = (String) names.nextElement();
int depth = getDepth(exceptionMapping, ex);
if (depth >= 0 && depth < deepest) {
if (depth >= 0 && (depth < deepest || (depth == deepest &&
dominantMapping != null && exceptionMapping.length() > dominantMapping.length()))) {
deepest = depth;
dominantMapping = exceptionMapping;
viewName = exceptionMappings.getProperty(exceptionMapping);

5
spring-webmvc-portlet/src/test/java/org/springframework/web/portlet/handler/SimpleMappingExceptionResolverTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@ -18,7 +18,6 @@ package org.springframework.web.portlet.handler; @@ -18,7 +18,6 @@ package org.springframework.web.portlet.handler;
import java.util.Collections;
import java.util.Properties;
import javax.portlet.WindowState;
import junit.framework.TestCase;
@ -206,7 +205,7 @@ public class SimpleMappingExceptionResolverTests extends TestCase { @@ -206,7 +205,7 @@ public class SimpleMappingExceptionResolverTests extends TestCase {
exceptionResolver.setMappedHandlers(Collections.singleton(handler1));
exceptionResolver.setExceptionMappings(props);
ModelAndView mav = exceptionResolver.resolveException(request, response, handler1, oddException);
assertEquals("error", mav.getViewName());
assertEquals("another-error", mav.getViewName());
}
public void testTwoMappingsThrowOddExceptionUseLongExceptionMapping() {

6
spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@ -51,7 +51,7 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti @@ -51,7 +51,7 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
private int order = Ordered.LOWEST_PRECEDENCE;
private Set mappedHandlers;
private Set<?> mappedHandlers;
private Class[] mappedHandlerClasses;
@ -76,7 +76,7 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti @@ -76,7 +76,7 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
* as fallback for all exceptions; any further HandlerExceptionResolvers in the chain will be
* ignored in this case.
*/
public void setMappedHandlers(Set mappedHandlers) {
public void setMappedHandlers(Set<?> mappedHandlers) {
this.mappedHandlers = mappedHandlers;
}

68
spring-webmvc/src/main/java/org/springframework/web/servlet/handler/SimpleMappingExceptionResolver.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@ -28,11 +28,12 @@ import org.springframework.web.servlet.ModelAndView; @@ -28,11 +28,12 @@ import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.WebUtils;
/**
* {@link org.springframework.web.servlet.HandlerExceptionResolver} implementation that allows for mapping exception
* class names to view names, either for a set of given handlers or for all handlers in the DispatcherServlet.
* {@link org.springframework.web.servlet.HandlerExceptionResolver} implementation
* that allows for mapping exception class names to view names, either for a set of
* given handlers or for all handlers in the DispatcherServlet.
*
* <p>Error views are analogous to error page JSPs, but can be used with any kind of exception including any checked
* one, with fine-granular mappings for specific handlers.
* <p>Error views are analogous to error page JSPs, but can be used with any kind of
* exception including any checked one, with fine-granular mappings for specific handlers.
*
* @author Juergen Hoeller
* @author Arjen Poutsma
@ -87,18 +88,20 @@ public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionReso @@ -87,18 +88,20 @@ public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionReso
}
/**
* Set the name of the default error view. This view will be returned if no specific mapping was found. <p>Default is
* none.
* Set the name of the default error view.
* This view will be returned if no specific mapping was found.
* <p>Default is none.
*/
public void setDefaultErrorView(String defaultErrorView) {
this.defaultErrorView = defaultErrorView;
}
/**
* Set the HTTP status code that this exception resolver will apply for a given resolved error view. Keys are
* view names; values are status codes.
* <p>Note that this error code will only get applied in case of a top-level request. It will not be set for an include
* request, since the HTTP status cannot be modified from within an include.
* Set the HTTP status code that this exception resolver will apply for a given
* resolved error view. Keys are view names; values are status codes.
* <p>Note that this error code will only get applied in case of a top-level request.
* It will not be set for an include request, since the HTTP status cannot be modified
* from within an include.
* <p>If not specified, the default status code will be applied.
* @see #setDefaultStatusCode(int)
*/
@ -127,12 +130,13 @@ public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionReso @@ -127,12 +130,13 @@ public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionReso
}
/**
* Set the default HTTP status code that this exception resolver will apply if it resolves an error view and if there
* is no status code mapping defined.
* <p>Note that this error code will only get applied in case of a top-level request. It will not be set for an
* include request, since the HTTP status cannot be modified from within an include.
* <p>If not specified, no status code will be applied, either leaving this to the controller or view, or keeping
* the servlet engine's default of 200 (OK).
* Set the default HTTP status code that this exception resolver will apply
* if it resolves an error view and if there is no status code mapping defined.
* <p>Note that this error code will only get applied in case of a top-level request.
* It will not be set for an include request, since the HTTP status cannot be modified
* from within an include.
* <p>If not specified, no status code will be applied, either leaving this to the
* controller or view, or keeping the servlet engine's default of 200 (OK).
* @param defaultStatusCode HTTP status code value, for example 500
* ({@link HttpServletResponse#SC_INTERNAL_SERVER_ERROR}) or 404 ({@link HttpServletResponse#SC_NOT_FOUND})
* @see #setStatusCodes(Properties)
@ -142,31 +146,34 @@ public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionReso @@ -142,31 +146,34 @@ public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionReso
}
/**
* Set the name of the model attribute as which the exception should be exposed. Default is "exception". <p>This can be
* either set to a different attribute name or to {@code null} for not exposing an exception attribute at all.
* Set the name of the model attribute as which the exception should be exposed.
* Default is "exception".
* <p>This can be either set to a different attribute name or to {@code null}
* for not exposing an exception attribute at all.
* @see #DEFAULT_EXCEPTION_ATTRIBUTE
*/
public void setExceptionAttribute(String exceptionAttribute) {
this.exceptionAttribute = exceptionAttribute;
}
/**
* Actually resolve the given exception that got thrown during on handler execution, returning a ModelAndView that
* represents a specific error page if appropriate. <p>May be overridden in subclasses, in order to apply specific
* exception checks. Note that this template method will be invoked <i>after</i> checking whether this resolved applies
* ("mappedHandlers" etc), so an implementation may simply proceed with its actual exception handling.
* Actually resolve the given exception that got thrown during on handler execution,
* returning a ModelAndView that represents a specific error page if appropriate.
* <p>May be overridden in subclasses, in order to apply specific exception checks.
* Note that this template method will be invoked <i>after</i> checking whether this
* resolved applies ("mappedHandlers" etc), so an implementation may simply proceed
* with its actual exception handling.
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler, or {@code null} if none chosen at the time of the exception (for example,
* if multipart resolution failed)
* @param handler the executed handler, or {@code null} if none chosen at the time
* of the exception (for example, if multipart resolution failed)
* @param ex the exception that got thrown during handler execution
* @return a corresponding ModelAndView to forward to, or {@code null} for default processing
*/
@Override
protected ModelAndView doResolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
// Expose ModelAndView for chosen error view.
String viewName = determineViewName(ex, request);
@ -231,7 +238,8 @@ public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionReso @@ -231,7 +238,8 @@ public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionReso
for (Enumeration<?> names = exceptionMappings.propertyNames(); names.hasMoreElements();) {
String exceptionMapping = (String) names.nextElement();
int depth = getDepth(exceptionMapping, ex);
if (depth >= 0 && depth < deepest) {
if (depth >= 0 && (depth < deepest || (depth == deepest &&
dominantMapping != null && exceptionMapping.length() > dominantMapping.length()))) {
deepest = depth;
dominantMapping = exceptionMapping;
viewName = exceptionMappings.getProperty(exceptionMapping);

22
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java

@ -20,10 +20,10 @@ import java.util.ArrayList; @@ -20,10 +20,10 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -86,7 +86,6 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe @@ -86,7 +86,6 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
/**
* Expose URI template variables, matrix variables, and producible media types in the request.
*
* @see HandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE
* @see HandlerMapping#MATRIX_VARIABLES_ATTRIBUTE
* @see HandlerMapping#PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
@ -149,19 +148,16 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe @@ -149,19 +148,16 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
/**
* Iterate all RequestMappingInfos once again, look if any match by URL at
* least and raise exceptions accordingly.
*
* @throws HttpRequestMethodNotSupportedException
* if there are matches by URL but not by HTTP method
* @throws HttpMediaTypeNotAcceptableException
* if there are matches by URL but not by consumable media types
* @throws HttpMediaTypeNotAcceptableException
* if there are matches by URL but not by producible media types
* @throws HttpRequestMethodNotSupportedException if there are matches by URL
* but not by HTTP method
* @throws HttpMediaTypeNotAcceptableException if there are matches by URL
* but not by consumable/producible media types
*/
@Override
protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> requestMappingInfos,
String lookupPath, HttpServletRequest request) throws ServletException {
Set<String> allowedMethods = new HashSet<String>(6);
Set<String> allowedMethods = new LinkedHashSet<String>(4);
Set<RequestMappingInfo> patternMatches = new HashSet<RequestMappingInfo>();
Set<RequestMappingInfo> patternAndMethodMatches = new HashSet<RequestMappingInfo>();
@ -193,12 +189,12 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe @@ -193,12 +189,12 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
if (patternAndMethodMatches.isEmpty()) {
consumableMediaTypes = getConsumableMediaTypes(request, patternMatches);
producibleMediaTypes = getProdicubleMediaTypes(request, patternMatches);
producibleMediaTypes = getProducibleMediaTypes(request, patternMatches);
paramConditions = getRequestParams(request, patternMatches);
}
else {
consumableMediaTypes = getConsumableMediaTypes(request, patternAndMethodMatches);
producibleMediaTypes = getProdicubleMediaTypes(request, patternAndMethodMatches);
producibleMediaTypes = getProducibleMediaTypes(request, patternAndMethodMatches);
paramConditions = getRequestParams(request, patternAndMethodMatches);
}
@ -236,7 +232,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe @@ -236,7 +232,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
return result;
}
private Set<MediaType> getProdicubleMediaTypes(HttpServletRequest request, Set<RequestMappingInfo> partialMatches) {
private Set<MediaType> getProducibleMediaTypes(HttpServletRequest request, Set<RequestMappingInfo> partialMatches) {
Set<MediaType> result = new HashSet<MediaType>();
for (RequestMappingInfo partialMatch : partialMatches) {
if (partialMatch.getProducesCondition().getMatchingCondition(request) == null) {

10
spring-webmvc/src/main/java/org/springframework/web/servlet/view/AbstractView.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@ -19,11 +19,10 @@ package org.springframework.web.servlet.view; @@ -19,11 +19,10 @@ package org.springframework.web.servlet.view;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -70,7 +69,7 @@ public abstract class AbstractView extends WebApplicationObjectSupport implement @@ -70,7 +69,7 @@ public abstract class AbstractView extends WebApplicationObjectSupport implement
private String requestContextAttribute;
/** Map of static attributes, keyed by attribute name (String) */
private final Map<String, Object> staticAttributes = new HashMap<String, Object>();
private final Map<String, Object> staticAttributes = new LinkedHashMap<String, Object>();
/** Whether or not the view should add path variables in the model */
private boolean exposePathVariables = true;
@ -269,6 +268,7 @@ public abstract class AbstractView extends WebApplicationObjectSupport implement @@ -269,6 +268,7 @@ public abstract class AbstractView extends WebApplicationObjectSupport implement
* Dynamic values take precedence over static attributes.
*/
protected Map<String, Object> createMergedOutputModel(Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) {
@SuppressWarnings("unchecked")
Map<String, Object> pathVars = this.exposePathVariables ?
@ -278,7 +278,7 @@ public abstract class AbstractView extends WebApplicationObjectSupport implement @@ -278,7 +278,7 @@ public abstract class AbstractView extends WebApplicationObjectSupport implement
int size = this.staticAttributes.size();
size += (model != null) ? model.size() : 0;
size += (pathVars != null) ? pathVars.size() : 0;
Map<String, Object> mergedModel = new HashMap<String, Object>(size);
Map<String, Object> mergedModel = new LinkedHashMap<String, Object>(size);
mergedModel.putAll(this.staticAttributes);
if (pathVars != null) {
mergedModel.putAll(pathVars);

4
spring-webmvc/src/test/java/org/springframework/web/servlet/handler/SimpleMappingExceptionResolverTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@ -249,7 +249,7 @@ public class SimpleMappingExceptionResolverTests { @@ -249,7 +249,7 @@ public class SimpleMappingExceptionResolverTests {
exceptionResolver.setMappedHandlers(Collections.singleton(handler1));
exceptionResolver.setExceptionMappings(props);
ModelAndView mav = exceptionResolver.resolveException(request, response, handler1, oddException);
assertEquals("error", mav.getViewName());
assertEquals("another-error", mav.getViewName());
}
@Test

79
spring-webmvc/src/test/java/org/springframework/web/servlet/view/document/PdfViewTests.java

@ -1,79 +0,0 @@ @@ -1,79 +0,0 @@
/*
* Copyright 2002-2012 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.view.document;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.lowagie.text.Document;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.PdfWriter;
import junit.framework.TestCase;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
/**
* @author Alef Arendsen
* @author Juergen Hoeller
*/
public class PdfViewTests extends TestCase {
public void testPdf() throws Exception {
final String text = "this should be in the PDF";
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
AbstractPdfView pdfView = new AbstractPdfView() {
@Override
protected void buildPdfDocument(Map model, Document document, PdfWriter writer,
HttpServletRequest request, HttpServletResponse response) throws Exception {
document.add(new Paragraph(text));
}
};
pdfView.render(new HashMap(), request, response);
byte[] pdfContent = response.getContentAsByteArray();
assertEquals("correct response content type", "application/pdf", response.getContentType());
assertEquals("correct response content length", pdfContent.length, response.getContentLength());
// rebuild iText document for comparison
Document document = new Document(PageSize.A4);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter writer = PdfWriter.getInstance(document, baos);
writer.setViewerPreferences(PdfWriter.AllowPrinting | PdfWriter.PageLayoutSinglePage);
document.open();
document.add(new Paragraph(text));
document.close();
byte[] baosContent = baos.toByteArray();
assertEquals("correct size", pdfContent.length, baosContent.length);
int diffCount = 0;
for (int i = 0; i < pdfContent.length; i++) {
if (pdfContent[i] != baosContent[i]) {
diffCount++;
}
}
assertTrue("difference only in encryption", diffCount < 70);
}
}

20
spring-webmvc/src/test/java/org/springframework/web/servlet/view/feed/AtomFeedViewTest.java → spring-webmvc/src/test/java/org/springframework/web/servlet/view/feed/AtomFeedViewTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright ${YEAR} the original author or authors.
* Copyright 2002-2013 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,8 +17,8 @@ @@ -17,8 +17,8 @@
package org.springframework.web.servlet.view.feed;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
@ -27,16 +27,20 @@ import javax.servlet.http.HttpServletResponse; @@ -27,16 +27,20 @@ import javax.servlet.http.HttpServletResponse;
import com.sun.syndication.feed.atom.Content;
import com.sun.syndication.feed.atom.Entry;
import com.sun.syndication.feed.atom.Feed;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
import static org.custommonkey.xmlunit.XMLUnit.setIgnoreWhitespace;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
public class AtomFeedViewTest {
import static org.custommonkey.xmlunit.XMLAssert.*;
import static org.custommonkey.xmlunit.XMLUnit.*;
import static org.junit.Assert.assertEquals;
/**
* @author Arjen Poutsma
*/
public class AtomFeedViewTests {
private AbstractAtomFeedView view;
@ -51,9 +55,9 @@ public class AtomFeedViewTest { @@ -51,9 +55,9 @@ public class AtomFeedViewTest {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
Map<String, String> model = new HashMap<String, String>();
model.put("1", "This is entry 1");
Map<String, String> model = new LinkedHashMap<String, String>();
model.put("2", "This is entry 2");
model.put("1", "This is entry 1");
view.render(model, request, response);
assertEquals("Invalid content-type", "application/atom+xml", response.getContentType());

18
spring-webmvc/src/test/java/org/springframework/web/servlet/view/feed/RssFeedViewTest.java → spring-webmvc/src/test/java/org/springframework/web/servlet/view/feed/RssFeedViewTests.java

@ -17,8 +17,8 @@ @@ -17,8 +17,8 @@
package org.springframework.web.servlet.view.feed;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
@ -27,16 +27,20 @@ import javax.servlet.http.HttpServletResponse; @@ -27,16 +27,20 @@ import javax.servlet.http.HttpServletResponse;
import com.sun.syndication.feed.rss.Channel;
import com.sun.syndication.feed.rss.Description;
import com.sun.syndication.feed.rss.Item;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
import static org.custommonkey.xmlunit.XMLUnit.setIgnoreWhitespace;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
public class RssFeedViewTest {
import static org.custommonkey.xmlunit.XMLAssert.*;
import static org.custommonkey.xmlunit.XMLUnit.*;
import static org.junit.Assert.assertEquals;
/**
* @author Arjen Poutsma
*/
public class RssFeedViewTests {
private AbstractRssFeedView view;
@ -52,9 +56,9 @@ public class RssFeedViewTest { @@ -52,9 +56,9 @@ public class RssFeedViewTest {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
Map<String, String> model = new HashMap<String, String>();
model.put("1", "This is entry 1");
Map<String, String> model = new LinkedHashMap<String, String>();
model.put("2", "This is entry 2");
model.put("1", "This is entry 1");
view.render(model, request, response);
assertEquals("Invalid content-type", "application/rss+xml", response.getContentType());
Loading…
Cancel
Save