Browse Source

Merge branch '3.2.x' into cleanup-3.2.x

pull/229/head
Phillip Webb 13 years ago
parent
commit
4849205c6a
  1. 8
      CONTRIBUTING.md
  2. 2
      build.gradle
  3. 8
      buildSrc/src/main/groovy/org/springframework/build/gradle/MergePlugin.groovy
  4. 5
      gradle/publish-maven.gradle
  5. 27
      spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
  6. 66
      spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java
  7. 40
      spring-expression/src/main/java/org/springframework/expression/spel/ast/Literal.java
  8. 43
      spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java
  9. 3
      spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java
  10. 6
      spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.java
  11. 5
      spring-test/src/main/java/org/springframework/mock/web/MockPageContext.java
  12. 5
      spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java
  13. 5
      spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletContext.java
  14. 7
      spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletRequest.java
  15. 16
      spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletSession.java
  16. 21
      spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java
  17. 51
      spring-web/src/main/java/org/springframework/http/InvalidMediaTypeException.java
  18. 33
      spring-web/src/main/java/org/springframework/http/MediaType.java
  19. 30
      spring-web/src/test/java/org/springframework/http/MediaTypeTests.java
  20. 10
      src/dist/changelog.txt
  21. 2
      src/reference/docbook/new-in-3.2.xml

8
CONTRIBUTING.md

@ -87,7 +87,7 @@ present in the framework. @@ -87,7 +87,7 @@ present in the framework.
```java
/*
* 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.
@ -108,16 +108,16 @@ package ...; @@ -108,16 +108,16 @@ package ...;
## Update Apache license header to modified files as necessary
Always check the date range in the license header. For example, if you've
modified a file in 2012 whose header still reads
modified a file in 2013 whose header still reads
```java
* Copyright 2002-2011 the original author or authors.
```
then be sure to update it to 2012 appropriately
then be sure to update it to 2013 appropriately
```java
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
```
## Use @since tags for newly-added public API types and methods

2
build.gradle

@ -689,7 +689,7 @@ project("spring-test-mvc") { @@ -689,7 +689,7 @@ project("spring-test-mvc") {
description = "Spring Test MVC Framework"
merge.into = project(":spring-test")
dependencies {
provided(project(":spring-context"))
optional(project(":spring-context"))
provided(project(":spring-webmvc"))
provided("javax.servlet:javax.servlet-api:3.0.1")
optional("org.hamcrest:hamcrest-core:1.3")

8
buildSrc/src/main/groovy/org/springframework/build/gradle/MergePlugin.groovy

@ -128,7 +128,13 @@ class MergePlugin implements Plugin<Project> { @@ -128,7 +128,13 @@ class MergePlugin implements Plugin<Project> {
(ExcludeRule.GROUP_KEY) : it.group,
(ExcludeRule.MODULE_KEY) : it.module])
}
intoConfiguration.dependencies.addAll(configuration.dependencies)
configuration.dependencies.each {
def intoCompile = project.merge.into.configurations.getByName("compile")
// Protect against changing a compile scope dependency (SPR-10218)
if(!intoCompile.dependencies.contains(it)) {
intoConfiguration.dependencies.add(it)
}
}
project.merge.into.install.repositories.mavenInstaller.pom.scopeMappings.addMapping(
mapping.priority + 100, intoConfiguration, mapping.scope)
}

5
gradle/publish-maven.gradle

@ -13,6 +13,11 @@ def customizePom(pom, gradleProject) { @@ -13,6 +13,11 @@ def customizePom(pom, gradleProject) {
dep.scope == "test"
}
// sort to make pom dependencies order consistent to ease comparison of older poms
generatedPom.dependencies = generatedPom.dependencies.sort { dep ->
"$dep.scope:$dep.groupId:$dep.artifactId"
}
// add all items necessary for maven central publication
generatedPom.project {
name = gradleProject.description

27
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.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.
@ -54,6 +54,7 @@ import org.springframework.core.type.StandardAnnotationMetadata; @@ -54,6 +54,7 @@ import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import static org.springframework.context.annotation.MetadataUtils.*;
@ -228,7 +229,7 @@ class ConfigurationClassParser { @@ -228,7 +229,7 @@ class ConfigurationClassParser {
// process any @Import annotations
Set<String> imports = getImports(metadata.getClassName(), null, new HashSet<String>());
if (imports != null && !imports.isEmpty()) {
if (!CollectionUtils.isEmpty(imports)) {
processImport(configClass, imports.toArray(new String[imports.size()]), true);
}
@ -292,9 +293,8 @@ class ConfigurationClassParser { @@ -292,9 +293,8 @@ class ConfigurationClassParser {
* @return a set of all {@link Import#value() import values} or {@code null}
* @throws IOException if there is any problem reading metadata from the named class
*/
private Set<String> getImports(String className, Set<String> imports,
Set<String> visited) throws IOException {
if (visited.add(className)) {
private Set<String> getImports(String className, Set<String> imports, Set<String> visited) throws IOException {
if (visited.add(className) && !className.startsWith("java")) {
AnnotationMetadata metadata = metadataReaderFactory.getMetadataReader(className).getAnnotationMetadata();
for (String annotationType : metadata.getAnnotationTypes()) {
imports = getImports(annotationType, imports, visited);
@ -331,7 +331,7 @@ class ConfigurationClassParser { @@ -331,7 +331,7 @@ class ConfigurationClassParser {
throw new IllegalStateException(ex);
}
}
else if (new AssignableTypeFilter(ImportBeanDefinitionRegistrar.class).match(reader, metadataReaderFactory)) {
else if (new AssignableTypeFilter(ImportBeanDefinitionRegistrar.class).match(reader, this.metadataReaderFactory)) {
// the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
try {
ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(
@ -360,17 +360,16 @@ class ConfigurationClassParser { @@ -360,17 +360,16 @@ class ConfigurationClassParser {
private void invokeAwareMethods(ImportBeanDefinitionRegistrar registrar) {
if (registrar instanceof Aware) {
if (registrar instanceof ResourceLoaderAware) {
((ResourceLoaderAware) registrar).setResourceLoader(resourceLoader);
((ResourceLoaderAware) registrar).setResourceLoader(this.resourceLoader);
}
if (registrar instanceof BeanClassLoaderAware) {
ClassLoader classLoader =
registry instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) registry).getBeanClassLoader() :
resourceLoader.getClassLoader();
ClassLoader classLoader = (this.registry instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) this.registry).getBeanClassLoader() :
this.resourceLoader.getClassLoader());
((BeanClassLoaderAware) registrar).setBeanClassLoader(classLoader);
}
if (registrar instanceof BeanFactoryAware && registry instanceof BeanFactory) {
((BeanFactoryAware) registrar).setBeanFactory((BeanFactory) registry);
if (registrar instanceof BeanFactoryAware && this.registry instanceof BeanFactory) {
((BeanFactoryAware) registrar).setBeanFactory((BeanFactory) this.registry);
}
}
}
@ -398,6 +397,7 @@ class ConfigurationClassParser { @@ -398,6 +397,7 @@ class ConfigurationClassParser {
return this.importStack;
}
interface ImportRegistry {
String getImportingClassFor(String importedClass);
@ -470,4 +470,5 @@ class ConfigurationClassParser { @@ -470,4 +470,5 @@ class ConfigurationClassParser {
new Location(importStack.peek().getResource(), metadata));
}
}
}

66
spring-core/src/main/java/org/springframework/core/GenericTypeResolver.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.
@ -26,11 +26,7 @@ import java.lang.reflect.WildcardType; @@ -26,11 +26,7 @@ import java.lang.reflect.WildcardType;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
/**
@ -47,12 +43,11 @@ import org.springframework.util.ConcurrentReferenceHashMap; @@ -47,12 +43,11 @@ import org.springframework.util.ConcurrentReferenceHashMap;
*/
public abstract class GenericTypeResolver {
private static final Log logger = LogFactory.getLog(GenericTypeResolver.class);
/** Cache from Class to TypeVariable Map */
private static final Map<Class, Map<TypeVariable, Type>> typeVariableCache =
new ConcurrentReferenceHashMap<Class, Map<TypeVariable,Type>>();
/**
* Determine the target type for the given parameter specification.
* @param methodParam the method parameter specification
@ -93,7 +88,6 @@ public abstract class GenericTypeResolver { @@ -93,7 +88,6 @@ public abstract class GenericTypeResolver {
/**
* Determine the target type for the generic return type of the given method,
* where formal type variables are declared on the given class.
*
* @param method the method to introspect
* @param clazz the class to resolve type variables against
* @return the corresponding generic parameter or return type
@ -112,15 +106,12 @@ public abstract class GenericTypeResolver { @@ -112,15 +106,12 @@ public abstract class GenericTypeResolver {
* Determine the target type for the generic return type of the given
* <em>generic method</em>, where formal type variables are declared on
* the given method itself.
*
* <p>For example, given a factory method with the following signature,
* if {@code resolveReturnTypeForGenericMethod()} is invoked with the reflected
* method for {@code creatProxy()} and an {@code Object[]} array containing
* {@code MyService.class}, {@code resolveReturnTypeForGenericMethod()} will
* infer that the target return type is {@code MyService}.
*
* <pre>{@code public static <T> T createProxy(Class<T> clazz)}</pre>
*
* <h4>Possible Return Values</h4>
* <ul>
* <li>the target return type, if it can be inferred</li>
@ -134,27 +125,20 @@ public abstract class GenericTypeResolver { @@ -134,27 +125,20 @@ public abstract class GenericTypeResolver {
* Method#getGenericParameterTypes() formal argument list} for the given
* method</li>
* </ul>
*
* @param method the method to introspect, never {@code null}
* @param args the arguments that will be supplied to the method when it is
* invoked, never {@code null}
* @return the resolved target return type, the standard return type, or
* {@code null}
* @return the resolved target return type, the standard return type, or {@code null}
* @since 3.2
* @see #resolveReturnType
*/
public static Class<?> resolveReturnTypeForGenericMethod(Method method, Object[] args) {
Assert.notNull(method, "method must not be null");
Assert.notNull(args, "args must not be null");
if (logger.isDebugEnabled()) {
logger.debug(String.format("Resolving return type for [%s] with concrete method arguments [%s].",
method.toGenericString(), ObjectUtils.nullSafeToString(args)));
}
Assert.notNull(method, "Method must not be null");
Assert.notNull(args, "Argument array must not be null");
final TypeVariable<Method>[] declaredTypeVariables = method.getTypeParameters();
final Type genericReturnType = method.getGenericReturnType();
final Type[] methodArgumentTypes = method.getGenericParameterTypes();
TypeVariable<Method>[] declaredTypeVariables = method.getTypeParameters();
Type genericReturnType = method.getGenericReturnType();
Type[] methodArgumentTypes = method.getGenericParameterTypes();
// No declared type variables to inspect, so just return the standard return type.
if (declaredTypeVariables.length == 0) {
@ -172,11 +156,6 @@ public abstract class GenericTypeResolver { @@ -172,11 +156,6 @@ public abstract class GenericTypeResolver {
boolean locallyDeclaredTypeVariableMatchesReturnType = false;
for (TypeVariable<Method> currentTypeVariable : declaredTypeVariables) {
if (currentTypeVariable.equals(genericReturnType)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format(
"Found declared type variable [%s] that matches the target return type [%s].",
currentTypeVariable, genericReturnType));
}
locallyDeclaredTypeVariableMatchesReturnType = true;
break;
}
@ -184,39 +163,20 @@ public abstract class GenericTypeResolver { @@ -184,39 +163,20 @@ public abstract class GenericTypeResolver {
if (locallyDeclaredTypeVariableMatchesReturnType) {
for (int i = 0; i < methodArgumentTypes.length; i++) {
final Type currentMethodArgumentType = methodArgumentTypes[i];
Type currentMethodArgumentType = methodArgumentTypes[i];
if (currentMethodArgumentType.equals(genericReturnType)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format(
"Found method argument type at index [%s] that matches the target return type.", i));
}
return args[i].getClass();
}
if (currentMethodArgumentType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) currentMethodArgumentType;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (int j = 0; j < actualTypeArguments.length; j++) {
final Type typeArg = actualTypeArguments[j];
for (Type typeArg : actualTypeArguments) {
if (typeArg.equals(genericReturnType)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format(
"Found method argument type at index [%s] that is parameterized with a type argument that matches the target return type.",
i));
}
if (args[i] instanceof Class) {
return (Class<?>) args[i];
} else {
// Consider adding logic to determine the class of the
// J'th typeArg, if possible.
logger.info(String.format(
"Could not determine the target type for type argument [%s] for method [%s].",
typeArg, method.toGenericString()));
}
else {
// Consider adding logic to determine the class of the typeArg, if possible.
// For now, just fall back...
return method.getReturnType();
}

40
spring-expression/src/main/java/org/springframework/expression/spel/ast/Literal.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,23 +17,32 @@ @@ -17,23 +17,32 @@
package org.springframework.expression.spel.ast;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.*;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.InternalParseException;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.SpelParseException;
/**
* Common superclass for nodes representing literals (boolean, string, number, etc).
*
* @author Andy Clement
* @author Juergen Hoeller
*/
public abstract class Literal extends SpelNodeImpl {
protected String literalValue;
private final String originalValue;
public Literal(String payload, int pos) {
public Literal(String originalValue, int pos) {
super(pos);
this.literalValue = payload;
this.originalValue = originalValue;
}
public abstract TypedValue getLiteralValue();
public final String getOriginalValue() {
return this.originalValue;
}
@Override
public final TypedValue getValueInternal(ExpressionState state) throws SpelEvaluationException {
@ -50,10 +59,13 @@ public abstract class Literal extends SpelNodeImpl { @@ -50,10 +59,13 @@ public abstract class Literal extends SpelNodeImpl {
return toString();
}
public abstract TypedValue getLiteralValue();
/**
* Process the string form of a number, using the specified base if supplied and return an appropriate literal to
* hold it. Any suffix to indicate a long will be taken into account (either 'l' or 'L' is supported).
*
* @param numberToken the token holding the number as its payload (eg. 1234 or 0xCAFE)
* @param radix the base of number
* @return a subtype of Literal that can represent it
@ -62,7 +74,8 @@ public abstract class Literal extends SpelNodeImpl { @@ -62,7 +74,8 @@ public abstract class Literal extends SpelNodeImpl {
try {
int value = Integer.parseInt(numberToken, radix);
return new IntLiteral(numberToken, pos, value);
} catch (NumberFormatException nfe) {
}
catch (NumberFormatException nfe) {
throw new InternalParseException(new SpelParseException(pos>>16, nfe, SpelMessage.NOT_AN_INTEGER, numberToken));
}
}
@ -71,25 +84,26 @@ public abstract class Literal extends SpelNodeImpl { @@ -71,25 +84,26 @@ public abstract class Literal extends SpelNodeImpl {
try {
long value = Long.parseLong(numberToken, radix);
return new LongLiteral(numberToken, pos, value);
} catch (NumberFormatException nfe) {
}
catch (NumberFormatException nfe) {
throw new InternalParseException(new SpelParseException(pos>>16, nfe, SpelMessage.NOT_A_LONG, numberToken));
}
}
public static Literal getRealLiteral(String numberToken, int pos, boolean isFloat) {
try {
if (isFloat) {
float value = Float.parseFloat(numberToken);
return new FloatLiteral(numberToken, pos, value);
} else {
}
else {
double value = Double.parseDouble(numberToken);
return new RealLiteral(numberToken, pos, value);
}
} catch (NumberFormatException nfe) {
}
catch (NumberFormatException nfe) {
throw new InternalParseException(new SpelParseException(pos>>16, nfe, SpelMessage.NOT_A_REAL, numberToken));
}
}
}

43
spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.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.
@ -183,6 +183,8 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe @@ -183,6 +183,8 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
private int registeredWithDestination = 0;
private volatile boolean recovering = false;
private Runnable stopCallback;
private Object currentRecoveryMarker = new Object();
@ -758,6 +760,9 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe @@ -758,6 +760,9 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
super.establishSharedConnection();
}
catch (Exception ex) {
if (ex instanceof JMSException) {
invokeExceptionListener((JMSException) ex);
}
logger.debug("Could not establish shared JMS Connection - " +
"leaving it up to asynchronous invokers to establish a Connection as soon as possible", ex);
}
@ -796,7 +801,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe @@ -796,7 +801,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
/**
* Handle the given exception that arose during setup of a listener.
* Called for every such exception in every concurrent listener.
* <p>The default implementation logs the exception at info level
* <p>The default implementation logs the exception at warn level
* if not recovered yet, and at debug level if already recovered.
* Can be overridden in subclasses.
* @param ex the exception to handle
@ -837,7 +842,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe @@ -837,7 +842,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
/**
* Recover this listener container after a listener failed to set itself up,
* for example reestablishing the underlying Connection.
* for example re-establishing the underlying Connection.
* <p>The default implementation delegates to DefaultMessageListenerContainer's
* recovery-capable {@link #refreshConnectionUntilSuccessful()} method, which will
* try to re-establish a Connection to the JMS provider both for the shared
@ -846,8 +851,14 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe @@ -846,8 +851,14 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* @see #refreshDestination()
*/
protected void recoverAfterListenerSetupFailure() {
refreshConnectionUntilSuccessful();
refreshDestination();
this.recovering = true;
try {
refreshConnectionUntilSuccessful();
refreshDestination();
}
finally {
this.recovering = false;
}
}
/**
@ -856,9 +867,11 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe @@ -856,9 +867,11 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* Connection, so either needs to operate on the shared Connection or on a
* temporary Connection that just gets established for validation purposes.
* <p>The default implementation retries until it successfully established a
* Connection, for as long as this message listener container is active.
* Connection, for as long as this message listener container is running.
* Applies the specified recovery interval between retries.
* @see #setRecoveryInterval
* @see #start()
* @see #stop()
*/
protected void refreshConnectionUntilSuccessful() {
while (isRunning()) {
@ -874,16 +887,19 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe @@ -874,16 +887,19 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
break;
}
catch (Exception ex) {
if (ex instanceof JMSException) {
invokeExceptionListener((JMSException) ex);
}
StringBuilder msg = new StringBuilder();
msg.append("Could not refresh JMS Connection for destination '");
msg.append(getDestinationDescription()).append("' - retrying in ");
msg.append(this.recoveryInterval).append(" ms. Cause: ");
msg.append(ex instanceof JMSException ? JmsUtils.buildExceptionMessage((JMSException) ex) : ex.getMessage());
if (logger.isDebugEnabled()) {
logger.warn(msg, ex);
logger.error(msg, ex);
}
else {
logger.warn(msg);
logger.error(msg);
}
}
sleepInbetweenRecoveryAttempts();
@ -925,6 +941,17 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe @@ -925,6 +941,17 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
}
}
/**
* Return whether this listener container is currently in a recovery attempt.
* <p>May be used to detect recovery phases but also the end of a recovery phase,
* with {@code isRecovering()} switching to {@code false} after having been found
* to return {@code true} before.
* @see #recoverAfterListenerSetupFailure()
*/
public final boolean isRecovering() {
return this.recovering;
}
//-------------------------------------------------------------------------
// Inner classes used as internal adapters

3
spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java

@ -30,6 +30,7 @@ import java.util.Date; @@ -30,6 +30,7 @@ import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
@ -294,7 +295,7 @@ public class MockHttpServletRequest implements HttpServletRequest { @@ -294,7 +295,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
public Enumeration<String> getAttributeNames() {
checkActive();
return Collections.enumeration(this.attributes.keySet());
return Collections.enumeration(new LinkedHashSet<String>(this.attributes.keySet()));
}
public String getCharacterEncoding() {

6
spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.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.
@ -22,6 +22,7 @@ import java.util.Enumeration; @@ -22,6 +22,7 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
@ -50,6 +51,7 @@ public class MockHttpSession implements HttpSession { @@ -50,6 +51,7 @@ public class MockHttpSession implements HttpSession {
public static final String SESSION_COOKIE_NAME = "JSESSION";
private static int nextId = 1;
private final String id;
@ -141,7 +143,7 @@ public class MockHttpSession implements HttpSession { @@ -141,7 +143,7 @@ public class MockHttpSession implements HttpSession {
}
public Enumeration<String> getAttributeNames() {
return Collections.enumeration(this.attributes.keySet());
return Collections.enumeration(new LinkedHashSet<String>(this.attributes.keySet()));
}
public String[] getValueNames() {

5
spring-test/src/main/java/org/springframework/mock/web/MockPageContext.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.
@ -21,6 +21,7 @@ import java.io.UnsupportedEncodingException; @@ -21,6 +21,7 @@ import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import javax.el.ELContext;
import javax.servlet.Servlet;
@ -249,7 +250,7 @@ public class MockPageContext extends PageContext { @@ -249,7 +250,7 @@ public class MockPageContext extends PageContext {
}
public Enumeration<String> getAttributeNames() {
return Collections.enumeration(this.attributes.keySet());
return Collections.enumeration(new LinkedHashSet<String>(this.attributes.keySet()));
}
@SuppressWarnings("unchecked")

5
spring-test/src/main/java/org/springframework/mock/web/MockServletContext.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.
@ -29,7 +29,6 @@ import java.util.LinkedHashMap; @@ -29,7 +29,6 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.activation.FileTypeMap;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
@ -434,7 +433,7 @@ public class MockServletContext implements ServletContext { @@ -434,7 +433,7 @@ public class MockServletContext implements ServletContext {
}
public Enumeration<String> getAttributeNames() {
return Collections.enumeration(this.attributes.keySet());
return Collections.enumeration(new LinkedHashSet<String>(this.attributes.keySet()));
}
public void setAttribute(String name, Object value) {

5
spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletContext.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,7 +28,6 @@ import java.util.LinkedHashMap; @@ -28,7 +28,6 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import javax.activation.FileTypeMap;
import javax.portlet.PortletContext;
import javax.portlet.PortletRequestDispatcher;
@ -210,7 +209,7 @@ public class MockPortletContext implements PortletContext { @@ -210,7 +209,7 @@ public class MockPortletContext implements PortletContext {
}
public Enumeration<String> getAttributeNames() {
return new Vector<String>(this.attributes.keySet()).elements();
return Collections.enumeration(new LinkedHashSet<String>(this.attributes.keySet()));
}
public void setAttribute(String name, Object value) {

7
spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletRequest.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.
@ -21,13 +21,12 @@ import java.util.Collections; @@ -21,13 +21,12 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import javax.portlet.PortalContext;
import javax.portlet.PortletContext;
import javax.portlet.PortletMode;
@ -332,7 +331,7 @@ public class MockPortletRequest implements PortletRequest { @@ -332,7 +331,7 @@ public class MockPortletRequest implements PortletRequest {
public Enumeration<String> getAttributeNames() {
checkActive();
return new Vector<String>(this.attributes.keySet()).elements();
return Collections.enumeration(new LinkedHashSet<String>(this.attributes.keySet()));
}
public void setParameters(Map<String, String[]> parameters) {

16
spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletSession.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,10 +18,10 @@ package org.springframework.mock.web.portlet; @@ -18,10 +18,10 @@ package org.springframework.mock.web.portlet;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Vector;
import javax.portlet.PortletContext;
import javax.portlet.PortletSession;
import javax.servlet.http.HttpSessionBindingEvent;
@ -51,9 +51,9 @@ public class MockPortletSession implements PortletSession { @@ -51,9 +51,9 @@ public class MockPortletSession implements PortletSession {
private final PortletContext portletContext;
private final Map<String, Object> portletAttributes = new HashMap<String, Object>();
private final Map<String, Object> portletAttributes = new LinkedHashMap<String, Object>();
private final Map<String, Object> applicationAttributes = new HashMap<String, Object>();
private final Map<String, Object> applicationAttributes = new LinkedHashMap<String, Object>();
private boolean invalid = false;
@ -92,15 +92,15 @@ public class MockPortletSession implements PortletSession { @@ -92,15 +92,15 @@ public class MockPortletSession implements PortletSession {
}
public Enumeration<String> getAttributeNames() {
return new Vector<String>(this.portletAttributes.keySet()).elements();
return Collections.enumeration(new LinkedHashSet<String>(this.portletAttributes.keySet()));
}
public Enumeration<String> getAttributeNames(int scope) {
if (scope == PortletSession.PORTLET_SCOPE) {
return new Vector<String>(this.portletAttributes.keySet()).elements();
return Collections.enumeration(new LinkedHashSet<String>(this.portletAttributes.keySet()));
}
else if (scope == PortletSession.APPLICATION_SCOPE) {
return new Vector<String>(this.applicationAttributes.keySet()).elements();
return Collections.enumeration(new LinkedHashSet<String>(this.applicationAttributes.keySet()));
}
return null;
}

21
spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.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.
@ -41,7 +41,8 @@ import org.springframework.util.StringUtils; @@ -41,7 +41,8 @@ import org.springframework.util.StringUtils;
/**
* Utility methods for working with {@link ContextLoader ContextLoaders} and
* {@link SmartContextLoader SmartContextLoaders} and resolving resource locations,
* annotated classes, and active bean definition profiles.
* annotated classes, active bean definition profiles, and application context
* initializers.
*
* @author Sam Brannen
* @since 3.1
@ -50,6 +51,7 @@ import org.springframework.util.StringUtils; @@ -50,6 +51,7 @@ import org.springframework.util.StringUtils;
* @see ContextConfiguration
* @see ContextConfigurationAttributes
* @see ActiveProfiles
* @see ApplicationContextInitializer
* @see MergedContextConfiguration
*/
abstract class ContextLoaderUtils {
@ -78,7 +80,7 @@ abstract class ContextLoaderUtils { @@ -78,7 +80,7 @@ abstract class ContextLoaderUtils {
* either {@value #DEFAULT_CONTEXT_LOADER_CLASS_NAME}
* or {@value #DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME} will be used as the
* default context loader class name. For details on the class resolution
* process, see {@link #resolveContextLoaderClass()}.
* process, see {@link #resolveContextLoaderClass}.
*
* @param testClass the test class for which the {@code ContextLoader}
* should be resolved; must not be {@code null}
@ -89,8 +91,9 @@ abstract class ContextLoaderUtils { @@ -89,8 +91,9 @@ abstract class ContextLoaderUtils {
* {@code ContextLoader} class to use; may be {@code null} or <em>empty</em>
* @return the resolved {@code ContextLoader} for the supplied
* {@code testClass} (never {@code null})
* @see #resolveContextLoaderClass()
* @see #resolveContextLoaderClass
*/
@SuppressWarnings("javadoc")
static ContextLoader resolveContextLoader(Class<?> testClass,
List<ContextConfigurationAttributes> configAttributesList, String defaultContextLoaderClassName) {
Assert.notNull(testClass, "Class must not be null");
@ -348,11 +351,11 @@ abstract class ContextLoaderUtils { @@ -348,11 +351,11 @@ abstract class ContextLoaderUtils {
* @param defaultContextLoaderClassName the name of the default
* {@code ContextLoader} class to use (may be {@code null})
* @return the merged context configuration
* @see #resolveContextLoader()
* @see #resolveContextConfigurationAttributes()
* @see SmartContextLoader#processContextConfiguration()
* @see ContextLoader#processLocations()
* @see #resolveActiveProfiles()
* @see #resolveContextLoader
* @see #resolveContextConfigurationAttributes
* @see SmartContextLoader#processContextConfiguration
* @see ContextLoader#processLocations
* @see #resolveActiveProfiles
* @see MergedContextConfiguration
*/
static MergedContextConfiguration buildMergedContextConfiguration(Class<?> testClass,

51
spring-web/src/main/java/org/springframework/http/InvalidMediaTypeException.java

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
/*
* 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.
* 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.http;
/**
* Exception thrown from {@link MediaType#parseMediaType(String)} in case of
* encountering an invalid media type specification String.
*
* @author Juergen Hoeller
* @since 3.2.2
*/
@SuppressWarnings("serial")
public class InvalidMediaTypeException extends IllegalArgumentException {
private String mediaType;
/**
* Create a new InvalidMediaTypeException for the given media type.
* @param mediaType the offending media type
* @param msg a detail message indicating the invalid part
*/
public InvalidMediaTypeException(String mediaType, String msg) {
super("Invalid media type \"" + mediaType + "\": " + msg);
this.mediaType = mediaType;
}
/**
* Return the offending media type.
*/
public String getMediaType() {
return this.mediaType;
}
}

33
spring-web/src/main/java/org/springframework/http/MediaType.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,6 +17,7 @@ @@ -17,6 +17,7 @@
package org.springframework.http;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
@ -321,8 +322,8 @@ public class MediaType implements Comparable<MediaType> { @@ -321,8 +322,8 @@ public class MediaType implements Comparable<MediaType> {
* @throws IllegalArgumentException if any of the parameters contain illegal characters
*/
public MediaType(String type, String subtype, Map<String, String> parameters) {
Assert.hasLength(type, "'type' must not be empty");
Assert.hasLength(subtype, "'subtype' must not be empty");
Assert.hasLength(type, "type must not be empty");
Assert.hasLength(subtype, "subtype must not be empty");
checkToken(type);
checkToken(subtype);
this.type = type.toLowerCase(Locale.ENGLISH);
@ -347,11 +348,11 @@ public class MediaType implements Comparable<MediaType> { @@ -347,11 +348,11 @@ public class MediaType implements Comparable<MediaType> {
* @throws IllegalArgumentException in case of illegal characters
* @see <a href="http://tools.ietf.org/html/rfc2616#section-2.2">HTTP 1.1, section 2.2</a>
*/
private void checkToken(String s) {
for (int i=0; i < s.length(); i++ ) {
char ch = s.charAt(i);
private void checkToken(String token) {
for (int i=0; i < token.length(); i++ ) {
char ch = token.charAt(i);
if (!TOKEN.get(ch)) {
throw new IllegalArgumentException("Invalid token character '" + ch + "' in token \"" + s + "\"");
throw new IllegalArgumentException("Invalid token character '" + ch + "' in token \"" + token + "\"");
}
}
}
@ -681,7 +682,7 @@ public class MediaType implements Comparable<MediaType> { @@ -681,7 +682,7 @@ public class MediaType implements Comparable<MediaType> {
* Parse the given String into a single {@code MediaType}.
* @param mediaType the string to parse
* @return the media type
* @throws IllegalArgumentException if the string cannot be parsed
* @throws InvalidMediaTypeException if the string cannot be parsed
*/
public static MediaType parseMediaType(String mediaType) {
Assert.hasLength(mediaType, "'mediaType' must not be empty");
@ -694,15 +695,15 @@ public class MediaType implements Comparable<MediaType> { @@ -694,15 +695,15 @@ public class MediaType implements Comparable<MediaType> {
}
int subIndex = fullType.indexOf('/');
if (subIndex == -1) {
throw new IllegalArgumentException("\"" + mediaType + "\" does not contain '/'");
throw new InvalidMediaTypeException(mediaType, "does not contain '/'");
}
if (subIndex == fullType.length() - 1) {
throw new IllegalArgumentException("\"" + mediaType + "\" does not contain subtype after '/'");
throw new InvalidMediaTypeException(mediaType, "does not contain subtype after '/'");
}
String type = fullType.substring(0, subIndex);
String subtype = fullType.substring(subIndex + 1, fullType.length());
if (WILDCARD_TYPE.equals(type) && !WILDCARD_TYPE.equals(subtype)) {
throw new IllegalArgumentException("A wildcard type is legal only in '*/*' (all media types).");
throw new InvalidMediaTypeException(mediaType, "wildcard type is legal only in '*/*' (all media types)");
}
Map<String, String> parameters = null;
@ -719,7 +720,15 @@ public class MediaType implements Comparable<MediaType> { @@ -719,7 +720,15 @@ public class MediaType implements Comparable<MediaType> {
}
}
return new MediaType(type, subtype, parameters);
try {
return new MediaType(type, subtype, parameters);
}
catch (UnsupportedCharsetException ex) {
throw new InvalidMediaTypeException(mediaType, "unsupported charset '" + ex.getCharsetName() + "'");
}
catch (IllegalArgumentException ex) {
throw new InvalidMediaTypeException(mediaType, ex.getMessage());
}
}

30
spring-web/src/test/java/org/springframework/http/MediaTypeTests.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.
@ -97,12 +97,12 @@ public class MediaTypeTests { @@ -97,12 +97,12 @@ public class MediaTypeTests {
assertEquals("Invalid toString() returned", "text/plain;q=0.7", result);
}
@Test(expected= IllegalArgumentException.class)
@Test(expected = IllegalArgumentException.class)
public void slashInType() {
new MediaType("text/plain");
}
@Test(expected= IllegalArgumentException.class)
@Test(expected = IllegalArgumentException.class)
public void slashInSubtype() {
new MediaType("text", "/");
}
@ -122,57 +122,57 @@ public class MediaTypeTests { @@ -122,57 +122,57 @@ public class MediaTypeTests {
assertEquals("Invalid quality factor", 0.2D, mediaType.getQualityValue(), 0D);
}
@Test(expected = IllegalArgumentException.class)
@Test(expected = InvalidMediaTypeException.class)
public void parseMediaTypeNoSubtype() {
MediaType.parseMediaType("audio");
}
@Test(expected = IllegalArgumentException.class)
@Test(expected = InvalidMediaTypeException.class)
public void parseMediaTypeNoSubtypeSlash() {
MediaType.parseMediaType("audio/");
}
@Test(expected = IllegalArgumentException.class)
@Test(expected = InvalidMediaTypeException.class)
public void parseMediaTypeTypeRange() {
MediaType.parseMediaType("*/json");
}
@Test(expected = IllegalArgumentException.class)
@Test(expected = InvalidMediaTypeException.class)
public void parseMediaTypeIllegalType() {
MediaType.parseMediaType("audio(/basic");
}
@Test(expected = IllegalArgumentException.class)
@Test(expected = InvalidMediaTypeException.class)
public void parseMediaTypeIllegalSubtype() {
MediaType.parseMediaType("audio/basic)");
}
@Test(expected = IllegalArgumentException.class)
@Test(expected = InvalidMediaTypeException.class)
public void parseMediaTypeEmptyParameterAttribute() {
MediaType.parseMediaType("audio/*;=value");
}
@Test(expected = IllegalArgumentException.class)
@Test(expected = InvalidMediaTypeException.class)
public void parseMediaTypeEmptyParameterValue() {
MediaType.parseMediaType("audio/*;attr=");
}
@Test(expected = IllegalArgumentException.class)
@Test(expected = InvalidMediaTypeException.class)
public void parseMediaTypeIllegalParameterAttribute() {
MediaType.parseMediaType("audio/*;attr<=value");
}
@Test(expected = IllegalArgumentException.class)
@Test(expected = InvalidMediaTypeException.class)
public void parseMediaTypeIllegalParameterValue() {
MediaType.parseMediaType("audio/*;attr=v>alue");
}
@Test(expected = IllegalArgumentException.class)
@Test(expected = InvalidMediaTypeException.class)
public void parseMediaTypeIllegalQualityFactor() {
MediaType.parseMediaType("audio/basic;q=1.1");
}
@Test(expected = IllegalArgumentException.class)
@Test(expected = InvalidMediaTypeException.class)
public void parseMediaTypeIllegalCharset() {
MediaType.parseMediaType("text/html; charset=foo-bar");
}
@ -193,7 +193,7 @@ public class MediaTypeTests { @@ -193,7 +193,7 @@ public class MediaTypeTests {
assertEquals("'v>alue'", mediaType.getParameter("attr"));
}
@Test(expected = IllegalArgumentException.class)
@Test(expected = InvalidMediaTypeException.class)
public void parseMediaTypeIllegalQuotedParameterValue() {
MediaType.parseMediaType("audio/*;attr=\"");
}

10
src/dist/changelog.txt vendored

@ -3,6 +3,16 @@ SPRING FRAMEWORK CHANGELOG @@ -3,6 +3,16 @@ SPRING FRAMEWORK CHANGELOG
http://www.springsource.org
Changes in version 3.2.2 (2013-03-07)
--------------------------------------
* ConfigurationClassPostProcessor consistently uses ClassLoader, not loading core JDK annotations via ASM (SPR-10249)
* DefaultMessageListenerContainer invokes specified ExceptionListener for recovery exceptions as well (SPR-10230)
* DefaultMessageListenerContainer logs recovery failures at error level and exposes "isRecovering()" method (SPR-10230)
* MediaType throws dedicated InvalidMediaTypeException instead of generic IllegalArgumentException (SPR-10226)
* consistent use of LinkedHashMaps and independent getAttributeNames Enumeration in Servlet/Portlet mocks (SPR-10224)
Changes in version 3.2.1 (2013-01-24)
--------------------------------------

2
src/reference/docbook/new-in-3.2.xml

@ -57,7 +57,7 @@ @@ -57,7 +57,7 @@
<section xml:id="new-in-3.2-webmvc-content-negotiation">
<title>Content negotiation improvements</title>
<para>A <interfacename>ContentNeogtiationStrategy</interfacename> is now
<para>A <interfacename>ContentNegotiationStrategy</interfacename> is now
available for resolving the requested media types from an incoming
request. The available implementations are based on the file extension,
query parameter, the 'Accept' header, or a fixed content type.

Loading…
Cancel
Save