diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 65db556fabd..02d0bc12e2e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 ...; ## 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 diff --git a/build.gradle b/build.gradle index 19fe164e4dc..9ac71e790ab 100644 --- a/build.gradle +++ b/build.gradle @@ -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") diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/MergePlugin.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/MergePlugin.groovy index 097e65ab93c..510a2698c6a 100644 --- a/buildSrc/src/main/groovy/org/springframework/build/gradle/MergePlugin.groovy +++ b/buildSrc/src/main/groovy/org/springframework/build/gradle/MergePlugin.groovy @@ -128,7 +128,13 @@ class MergePlugin implements Plugin { (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) } diff --git a/gradle/publish-maven.gradle b/gradle/publish-maven.gradle index 528e78c15d5..d47098b4f12 100644 --- a/gradle/publish-maven.gradle +++ b/gradle/publish-maven.gradle @@ -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 diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index 2901e5cea0d..c03aca61ddf 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -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; 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 { // process any @Import annotations Set imports = getImports(metadata.getClassName(), null, new HashSet()); - if (imports != null && !imports.isEmpty()) { + if (!CollectionUtils.isEmpty(imports)) { processImport(configClass, imports.toArray(new String[imports.size()]), true); } @@ -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 getImports(String className, Set imports, - Set visited) throws IOException { - if (visited.add(className)) { + private Set getImports(String className, Set imports, Set 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 { 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 { 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 { return this.importStack; } + interface ImportRegistry { String getImportingClassFor(String importedClass); @@ -470,4 +470,5 @@ class ConfigurationClassParser { new Location(importStack.peek().getResource(), metadata)); } } + } diff --git a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java index cb9e8fba331..3842a741c74 100644 --- a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java +++ b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java @@ -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; 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; */ public abstract class GenericTypeResolver { - private static final Log logger = LogFactory.getLog(GenericTypeResolver.class); - /** Cache from Class to TypeVariable Map */ private static final Map> typeVariableCache = new ConcurrentReferenceHashMap>(); + /** * Determine the target type for the given parameter specification. * @param methodParam the method parameter specification @@ -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 { * Determine the target type for the generic return type of the given * generic method, where formal type variables are declared on * the given method itself. - * *

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}. - * *

{@code public static  T createProxy(Class clazz)}
- * *

Possible Return Values

*
    *
  • the target return type, if it can be inferred
  • @@ -134,27 +125,20 @@ public abstract class GenericTypeResolver { * Method#getGenericParameterTypes() formal argument list} for the given * method *
- * * @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[] declaredTypeVariables = method.getTypeParameters(); - final Type genericReturnType = method.getGenericReturnType(); - final Type[] methodArgumentTypes = method.getGenericParameterTypes(); + TypeVariable[] 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 { boolean locallyDeclaredTypeVariableMatchesReturnType = false; for (TypeVariable 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 { 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(); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Literal.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Literal.java index 19ddf7caacb..9b35a78a403 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Literal.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Literal.java @@ -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 @@ 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 { 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 { 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 { 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)); } } } - diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java index 91b15c319df..de0132dae16 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java @@ -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 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 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 /** * Handle the given exception that arose during setup of a listener. * Called for every such exception in every concurrent listener. - *

The default implementation logs the exception at info level + *

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 /** * 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. *

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 * @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 * Connection, so either needs to operate on the shared Connection or on a * temporary Connection that just gets established for validation purposes. *

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 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 } } + /** + * Return whether this listener container is currently in a recovery attempt. + *

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 diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java index d609f39e8fc..0d29289c77e 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java @@ -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 { public Enumeration getAttributeNames() { checkActive(); - return Collections.enumeration(this.attributes.keySet()); + return Collections.enumeration(new LinkedHashSet(this.attributes.keySet())); } public String getCharacterEncoding() { diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.java index 1855230d2c5..548c2016d15 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.java @@ -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; 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 { 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 { } public Enumeration getAttributeNames() { - return Collections.enumeration(this.attributes.keySet()); + return Collections.enumeration(new LinkedHashSet(this.attributes.keySet())); } public String[] getValueNames() { diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockPageContext.java b/spring-test/src/main/java/org/springframework/mock/web/MockPageContext.java index b492c1fa980..2c5d07e8e3c 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockPageContext.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockPageContext.java @@ -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; 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 { } public Enumeration getAttributeNames() { - return Collections.enumeration(this.attributes.keySet()); + return Collections.enumeration(new LinkedHashSet(this.attributes.keySet())); } @SuppressWarnings("unchecked") diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java b/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java index 562179ceac8..40fbabd1a17 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java @@ -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; 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 { } public Enumeration getAttributeNames() { - return Collections.enumeration(this.attributes.keySet()); + return Collections.enumeration(new LinkedHashSet(this.attributes.keySet())); } public void setAttribute(String name, Object value) { diff --git a/spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletContext.java b/spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletContext.java index b1193ff5e1e..39dc9f5b294 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletContext.java +++ b/spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletContext.java @@ -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; 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 { } public Enumeration getAttributeNames() { - return new Vector(this.attributes.keySet()).elements(); + return Collections.enumeration(new LinkedHashSet(this.attributes.keySet())); } public void setAttribute(String name, Object value) { diff --git a/spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletRequest.java b/spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletRequest.java index 70e90567b0c..38b517351a7 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletRequest.java @@ -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; 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 { public Enumeration getAttributeNames() { checkActive(); - return new Vector(this.attributes.keySet()).elements(); + return Collections.enumeration(new LinkedHashSet(this.attributes.keySet())); } public void setParameters(Map parameters) { diff --git a/spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletSession.java b/spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletSession.java index 93800340b85..5eaec799063 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletSession.java +++ b/spring-test/src/main/java/org/springframework/mock/web/portlet/MockPortletSession.java @@ -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; 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 { private final PortletContext portletContext; - private final Map portletAttributes = new HashMap(); + private final Map portletAttributes = new LinkedHashMap(); - private final Map applicationAttributes = new HashMap(); + private final Map applicationAttributes = new LinkedHashMap(); private boolean invalid = false; @@ -92,15 +92,15 @@ public class MockPortletSession implements PortletSession { } public Enumeration getAttributeNames() { - return new Vector(this.portletAttributes.keySet()).elements(); + return Collections.enumeration(new LinkedHashSet(this.portletAttributes.keySet())); } public Enumeration getAttributeNames(int scope) { if (scope == PortletSession.PORTLET_SCOPE) { - return new Vector(this.portletAttributes.keySet()).elements(); + return Collections.enumeration(new LinkedHashSet(this.portletAttributes.keySet())); } else if (scope == PortletSession.APPLICATION_SCOPE) { - return new Vector(this.applicationAttributes.keySet()).elements(); + return Collections.enumeration(new LinkedHashSet(this.applicationAttributes.keySet())); } return null; } diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java index 47ee0c78cf0..8016c5d7c77 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java @@ -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; /** * 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; * @see ContextConfiguration * @see ContextConfigurationAttributes * @see ActiveProfiles + * @see ApplicationContextInitializer * @see MergedContextConfiguration */ 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 { * {@code ContextLoader} class to use; may be {@code null} or empty * @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 configAttributesList, String defaultContextLoaderClassName) { Assert.notNull(testClass, "Class must not be null"); @@ -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, diff --git a/spring-web/src/main/java/org/springframework/http/InvalidMediaTypeException.java b/spring-web/src/main/java/org/springframework/http/InvalidMediaTypeException.java new file mode 100644 index 00000000000..358c9c8226a --- /dev/null +++ b/spring-web/src/main/java/org/springframework/http/InvalidMediaTypeException.java @@ -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; + } + +} diff --git a/spring-web/src/main/java/org/springframework/http/MediaType.java b/spring-web/src/main/java/org/springframework/http/MediaType.java index 99e1b2d1249..9f527c7c8ce 100644 --- a/spring-web/src/main/java/org/springframework/http/MediaType.java +++ b/spring-web/src/main/java/org/springframework/http/MediaType.java @@ -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 @@ 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 { * @throws IllegalArgumentException if any of the parameters contain illegal characters */ public MediaType(String type, String subtype, Map 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 { * @throws IllegalArgumentException in case of illegal characters * @see HTTP 1.1, section 2.2 */ - 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 { * 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 { } 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 parameters = null; @@ -719,7 +720,15 @@ public class MediaType implements Comparable { } } - 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()); + } } diff --git a/spring-web/src/test/java/org/springframework/http/MediaTypeTests.java b/spring-web/src/test/java/org/springframework/http/MediaTypeTests.java index 906c8dec588..d5657d199e7 100644 --- a/spring-web/src/test/java/org/springframework/http/MediaTypeTests.java +++ b/spring-web/src/test/java/org/springframework/http/MediaTypeTests.java @@ -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 { 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 { 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 { assertEquals("'v>alue'", mediaType.getParameter("attr")); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = InvalidMediaTypeException.class) public void parseMediaTypeIllegalQuotedParameterValue() { MediaType.parseMediaType("audio/*;attr=\""); } diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index 25b90985b9b..70defd90ddc 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -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) -------------------------------------- diff --git a/src/reference/docbook/new-in-3.2.xml b/src/reference/docbook/new-in-3.2.xml index 1f3d04e3ba4..4ffd3cf6cdf 100644 --- a/src/reference/docbook/new-in-3.2.xml +++ b/src/reference/docbook/new-in-3.2.xml @@ -57,7 +57,7 @@

Content negotiation improvements - A ContentNeogtiationStrategy is now + A ContentNegotiationStrategy 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.